mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-20 21:11:33 +00:00
Merge upstream/master
This commit is contained in:
commit
cc23a968d1
178 changed files with 4724 additions and 1137 deletions
|
@ -1,26 +1,26 @@
|
||||||
# use the official gcc image, based on debian
|
stages:
|
||||||
# can use verions as well, like gcc:5.2
|
- build
|
||||||
# see https://hub.docker.com/_/gcc/
|
|
||||||
image: gcc
|
|
||||||
|
|
||||||
cache:
|
Debian:
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- linux
|
||||||
|
image: gcc
|
||||||
|
cache:
|
||||||
key: apt-cache
|
key: apt-cache
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
|
before_script:
|
||||||
before_script:
|
|
||||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||||
- apt-get update -yq
|
- apt-get update -yq
|
||||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
|
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
|
||||||
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
|
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
|
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
|
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
|
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
|
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
|
||||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
||||||
|
|
||||||
build:
|
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||||
|
@ -30,13 +30,40 @@ build:
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/artifacts/
|
- build/artifacts/
|
||||||
# depending on your build setup it's most likely a good idea to cache outputs to reduce the build time
|
MacOS:
|
||||||
|
tags:
|
||||||
|
- macos
|
||||||
|
- xcode
|
||||||
|
except:
|
||||||
|
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
||||||
|
stage: build
|
||||||
|
allow_failure: true
|
||||||
|
script:
|
||||||
|
- rm -fr build/* # remove anything in the build directory
|
||||||
|
- CI/before_install.osx.sh
|
||||||
|
- CI/before_script.osx.sh
|
||||||
|
- cd build; make -j2 package
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/OpenMW-*.dmg
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
tags:
|
||||||
|
- win10
|
||||||
|
- msvc2017
|
||||||
|
except:
|
||||||
|
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
||||||
|
stage: build
|
||||||
|
allow_failure: true
|
||||||
|
script:
|
||||||
|
# - env # turn on for debugging
|
||||||
|
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
||||||
|
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
|
||||||
|
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
|
||||||
|
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- "*.o"
|
- deps
|
||||||
|
artifacts:
|
||||||
# TODO: run tests using the binary built before
|
paths:
|
||||||
#test:
|
- "*.zip"
|
||||||
# stage: test
|
|
||||||
# script:
|
|
||||||
# - ls
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ env:
|
||||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||||
# via the "travis encrypt" command using the project repo's public key
|
# via the "travis encrypt" command using the project repo's public key
|
||||||
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
||||||
- macos_qt_formula=qt
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
|
|
|
@ -22,6 +22,7 @@ Programmers
|
||||||
alexanderkjall
|
alexanderkjall
|
||||||
Alexander Nadeau (wareya)
|
Alexander Nadeau (wareya)
|
||||||
Alexander Olofsson (Ace)
|
Alexander Olofsson (Ace)
|
||||||
|
Alex S (docwest)
|
||||||
Allofich
|
Allofich
|
||||||
Andrei Kortunov (akortunov)
|
Andrei Kortunov (akortunov)
|
||||||
AnyOldName3
|
AnyOldName3
|
||||||
|
@ -63,6 +64,7 @@ Programmers
|
||||||
Evgeniy Mineev (sandstranger)
|
Evgeniy Mineev (sandstranger)
|
||||||
Federico Guerra (FedeWar)
|
Federico Guerra (FedeWar)
|
||||||
Fil Krynicki (filkry)
|
Fil Krynicki (filkry)
|
||||||
|
Finbar Crago (finbar-crago)
|
||||||
Florian Weber (Florianjw)
|
Florian Weber (Florianjw)
|
||||||
Gašper Sedej
|
Gašper Sedej
|
||||||
gugus/gus
|
gugus/gus
|
||||||
|
@ -141,6 +143,7 @@ Programmers
|
||||||
Rohit Nirmal
|
Rohit Nirmal
|
||||||
Roman Melnik (Kromgart)
|
Roman Melnik (Kromgart)
|
||||||
Roman Proskuryakov (kpp)
|
Roman Proskuryakov (kpp)
|
||||||
|
Roman Siromakha (elsid)
|
||||||
Sandy Carter (bwrsandman)
|
Sandy Carter (bwrsandman)
|
||||||
Scott Howard
|
Scott Howard
|
||||||
scrawl
|
scrawl
|
||||||
|
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -2,14 +2,18 @@
|
||||||
------
|
------
|
||||||
|
|
||||||
Bug #1990: Sunrise/sunset not set correct
|
Bug #1990: Sunrise/sunset not set correct
|
||||||
|
Bug #2131: Lustidrike's spell misses the player every time
|
||||||
Bug #2222: Fatigue's effect on selling price is backwards
|
Bug #2222: Fatigue's effect on selling price is backwards
|
||||||
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
||||||
|
Bug #2455: Creatures attacks degrade armor
|
||||||
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
||||||
|
Bug #2626: Resurrecting the player does not resume the game
|
||||||
Bug #2772: Non-existing class or faction freezes the game
|
Bug #2772: Non-existing class or faction freezes the game
|
||||||
Bug #2835: Player able to slowly move when overencumbered
|
Bug #2835: Player able to slowly move when overencumbered
|
||||||
Bug #2852: No murder bounty when a player follower commits murder
|
Bug #2852: No murder bounty when a player follower commits murder
|
||||||
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
|
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
|
||||||
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
|
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
|
||||||
|
Bug #3249: Fixed revert function not updating views properly
|
||||||
Bug #3374: Touch spells not hitting kwama foragers
|
Bug #3374: Touch spells not hitting kwama foragers
|
||||||
Bug #3486: [Mod] NPC Commands does not work
|
Bug #3486: [Mod] NPC Commands does not work
|
||||||
Bug #3591: Angled hit distance too low
|
Bug #3591: Angled hit distance too low
|
||||||
|
@ -17,19 +21,24 @@
|
||||||
Bug #3876: Landscape texture painting is misaligned
|
Bug #3876: Landscape texture painting is misaligned
|
||||||
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
||||||
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
|
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
|
||||||
|
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
|
||||||
Bug #3993: Terrain texture blending map is not upscaled
|
Bug #3993: Terrain texture blending map is not upscaled
|
||||||
Bug #3997: Almalexia doesn't pace
|
Bug #3997: Almalexia doesn't pace
|
||||||
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
||||||
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
|
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
|
||||||
|
Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts
|
||||||
Bug #4125: OpenMW logo cropped on bugtracker
|
Bug #4125: OpenMW logo cropped on bugtracker
|
||||||
Bug #4215: OpenMW shows book text after last <BR> tag
|
Bug #4215: OpenMW shows book text after last EOL tag
|
||||||
Bug #4221: Characters get stuck in V-shaped terrain
|
Bug #4221: Characters get stuck in V-shaped terrain
|
||||||
|
Bug #4230: AiTravel package issues break some Tribunal quests
|
||||||
Bug #4251: Stationary NPCs do not return to their position after combat
|
Bug #4251: Stationary NPCs do not return to their position after combat
|
||||||
|
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
|
||||||
Bug #4286: Scripted animations can be interrupted
|
Bug #4286: Scripted animations can be interrupted
|
||||||
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
|
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
|
||||||
Bug #4293: Faction members are not aware of faction ownerships in barter
|
Bug #4293: Faction members are not aware of faction ownerships in barter
|
||||||
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
|
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
|
||||||
Bug #4327: Missing animations during spell/weapon stance switching
|
Bug #4327: Missing animations during spell/weapon stance switching
|
||||||
|
Bug #4358: Running animation is interrupted when magic mode is toggled
|
||||||
Bug #4368: Settings window ok button doesn't have key focus by default
|
Bug #4368: Settings window ok button doesn't have key focus by default
|
||||||
Bug #4393: NPCs walk back to where they were after using ResetActors
|
Bug #4393: NPCs walk back to where they were after using ResetActors
|
||||||
Bug #4416: Handle exception if we try to play non-music file
|
Bug #4416: Handle exception if we try to play non-music file
|
||||||
|
@ -46,16 +55,40 @@
|
||||||
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
|
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
|
||||||
Bug #4458: AiWander console command handles idle chances incorrectly
|
Bug #4458: AiWander console command handles idle chances incorrectly
|
||||||
Bug #4459: NotCell dialogue condition doesn't support partial matches
|
Bug #4459: NotCell dialogue condition doesn't support partial matches
|
||||||
|
Bug #4460: Script function "Equip" doesn't bypass beast restrictions
|
||||||
Bug #4461: "Open" spell from non-player caster isn't a crime
|
Bug #4461: "Open" spell from non-player caster isn't a crime
|
||||||
|
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
|
||||||
Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal
|
Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal
|
||||||
Bug #4471: Retrieve SDL window settings instead of using magic numbers
|
Bug #4474: No fallback when getVampireHead fails
|
||||||
|
Bug #4475: Scripted animations should not cause movement
|
||||||
|
Bug #4479: "Game" category on Advanced page is getting too long
|
||||||
|
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
|
||||||
|
Bug #4489: Goodbye doesn't block dialogue hyperlinks
|
||||||
|
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
|
||||||
|
Bug #4494: Training cap based off Base Skill instead of Modified Skill
|
||||||
|
Bug #4495: Crossbow animations blending is buggy
|
||||||
|
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
|
||||||
|
Bug #4497: File names starting with x or X are not classified as animation
|
||||||
|
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
|
||||||
|
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
|
||||||
|
Bug #4519: Knockdown does not discard movement in the 1st-person mode
|
||||||
|
Feature #2606: Editor: Implemented (optional) case sensitive global search
|
||||||
|
Feature #3083: Play animation when NPC is casting spell via script
|
||||||
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
|
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
|
||||||
|
Feature #3641: Editor: Limit FPS in 3d preview window
|
||||||
Feature #4222: 360° screenshots
|
Feature #4222: 360° screenshots
|
||||||
Feature #4256: Implement ToggleBorders (TB) console command
|
Feature #4256: Implement ToggleBorders (TB) console command
|
||||||
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
||||||
Feature #4345: Add equivalents for the command line commands to Launcher
|
Feature #4345: Add equivalents for the command line commands to Launcher
|
||||||
|
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
|
||||||
Feature #4444: Per-group KF-animation files support
|
Feature #4444: Per-group KF-animation files support
|
||||||
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
|
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
|
||||||
|
Feature #4488: Make water shader rougher during rain
|
||||||
|
Feature #4012: Editor: Write a log file if OpenCS crashes
|
||||||
|
Feature #4509: Show count of enchanted items in stack in the spells list
|
||||||
|
Feature #4512: Editor: Use markers for lights and creatures levelled lists
|
||||||
|
Task #2490: Don't open command prompt window on Release-mode builds automatically
|
||||||
|
Task #4545: Enable is_pod string test
|
||||||
|
|
||||||
0.44.0
|
0.44.0
|
||||||
------
|
------
|
||||||
|
|
|
@ -4,7 +4,7 @@ brew update
|
||||||
|
|
||||||
brew outdated cmake || brew upgrade cmake
|
brew outdated cmake || brew upgrade cmake
|
||||||
brew outdated pkgconfig || brew upgrade pkgconfig
|
brew outdated pkgconfig || brew upgrade pkgconfig
|
||||||
brew install $macos_qt_formula
|
brew install qt
|
||||||
|
|
||||||
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
|
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
|
||||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# set -x # turn-on for debugging
|
||||||
|
|
||||||
MISSINGTOOLS=0
|
MISSINGTOOLS=0
|
||||||
|
|
||||||
|
@ -76,7 +77,6 @@ while [ $# -gt 0 ]; do
|
||||||
h )
|
h )
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 [-cdehkpuvV]
|
Usage: $0 [-cdehkpuvV]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-c <Release/Debug>
|
-c <Release/Debug>
|
||||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||||
|
@ -232,10 +232,9 @@ fi
|
||||||
case $VS_VERSION in
|
case $VS_VERSION in
|
||||||
15|15.0|2017 )
|
15|15.0|2017 )
|
||||||
GENERATOR="Visual Studio 15 2017"
|
GENERATOR="Visual Studio 15 2017"
|
||||||
TOOLSET="vc140"
|
TOOLSET="vc141"
|
||||||
TOOLSET_REAL="vc141"
|
|
||||||
MSVC_REAL_VER="15"
|
MSVC_REAL_VER="15"
|
||||||
MSVC_VER="14"
|
MSVC_VER="14.1"
|
||||||
MSVC_YEAR="2015"
|
MSVC_YEAR="2015"
|
||||||
MSVC_DISPLAY_YEAR="2017"
|
MSVC_DISPLAY_YEAR="2017"
|
||||||
;;
|
;;
|
||||||
|
@ -243,9 +242,8 @@ case $VS_VERSION in
|
||||||
14|14.0|2015 )
|
14|14.0|2015 )
|
||||||
GENERATOR="Visual Studio 14 2015"
|
GENERATOR="Visual Studio 14 2015"
|
||||||
TOOLSET="vc140"
|
TOOLSET="vc140"
|
||||||
TOOLSET_REAL="vc140"
|
|
||||||
MSVC_REAL_VER="14"
|
MSVC_REAL_VER="14"
|
||||||
MSVC_VER="14"
|
MSVC_VER="14.0"
|
||||||
MSVC_YEAR="2015"
|
MSVC_YEAR="2015"
|
||||||
MSVC_DISPLAY_YEAR="2015"
|
MSVC_DISPLAY_YEAR="2015"
|
||||||
;;
|
;;
|
||||||
|
@ -253,9 +251,8 @@ case $VS_VERSION in
|
||||||
12|12.0|2013 )
|
12|12.0|2013 )
|
||||||
GENERATOR="Visual Studio 12 2013"
|
GENERATOR="Visual Studio 12 2013"
|
||||||
TOOLSET="vc120"
|
TOOLSET="vc120"
|
||||||
TOOLSET_REAL="vc120"
|
|
||||||
MSVC_REAL_VER="12"
|
MSVC_REAL_VER="12"
|
||||||
MSVC_VER="12"
|
MSVC_VER="12.0"
|
||||||
MSVC_YEAR="2013"
|
MSVC_YEAR="2013"
|
||||||
MSVC_DISPLAY_YEAR="2013"
|
MSVC_DISPLAY_YEAR="2013"
|
||||||
;;
|
;;
|
||||||
|
@ -325,9 +322,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
download "Boost 1.61.0" \
|
download "Boost 1.67.0" \
|
||||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
|
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||||
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bullet
|
# Bullet
|
||||||
|
@ -365,8 +362,8 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
QT_SUFFIX=""
|
QT_SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
download "Qt 5.7.2" \
|
download "Qt 5.7.0" \
|
||||||
"https://download.qt.io/official_releases/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
|
"https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
|
||||||
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
|
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
|
||||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
||||||
"qt-5-install.qs"
|
"qt-5-install.qs"
|
||||||
|
@ -403,9 +400,9 @@ echo
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
printf "Boost 1.61.0... "
|
printf "Boost 1.67.0... "
|
||||||
else
|
else
|
||||||
if [ $MSVC_VER -eq 12 ]; then
|
if [ $MSVC_VER -eq 12.0 ]; then
|
||||||
printf "Boost 1.58.0 AppVeyor... "
|
printf "Boost 1.58.0 AppVeyor... "
|
||||||
else
|
else
|
||||||
printf "Boost 1.67.0 AppVeyor... "
|
printf "Boost 1.67.0 AppVeyor... "
|
||||||
|
@ -417,17 +414,28 @@ fi
|
||||||
|
|
||||||
BOOST_SDK="$(real_pwd)/Boost"
|
BOOST_SDK="$(real_pwd)/Boost"
|
||||||
|
|
||||||
if [ -d Boost ] && grep "BOOST_VERSION 106100" Boost/boost/version.hpp > /dev/null; then
|
# Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names
|
||||||
|
# We work around this by installing to root of the current working drive and then move it to our deps
|
||||||
|
# get the current working drive's root, we'll install to that temporarily
|
||||||
|
CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp"
|
||||||
|
CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||||
|
if [ -d CWD_DRIVE_ROOT_BASH ]; then
|
||||||
|
printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf Boost
|
rm -rf Boost
|
||||||
"${DEPS}/boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe" //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
|
CI_EXTRA_INNO_OPTIONS=""
|
||||||
|
[ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
|
||||||
|
"${DEPS}/boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
|
||||||
|
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.0"
|
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}"
|
||||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
else
|
else
|
||||||
# Appveyor unstable has all the boost we need already
|
# Appveyor unstable has all the boost we need already
|
||||||
|
@ -444,19 +452,17 @@ fi
|
||||||
|
|
||||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
|
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
|
||||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET_REAL}"
|
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Bullet
|
# Bullet
|
||||||
printf "Bullet 2.86... "
|
printf "Bullet 2.86... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d Bullet ]; then
|
if [ -d Bullet ]; then
|
||||||
printf -- "Exists. (No version checking) "
|
printf -- "Exists. (No version checking) "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
|
@ -464,49 +470,38 @@ printf "Bullet 2.86... "
|
||||||
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||||
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export BULLET_ROOT="$(real_pwd)/Bullet"
|
export BULLET_ROOT="$(real_pwd)/Bullet"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
printf "FFmpeg 3.2.4... "
|
printf "FFmpeg 3.2.4... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf FFmpeg
|
rm -rf FFmpeg
|
||||||
|
|
||||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
|
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
|
||||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
|
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
|
||||||
|
|
||||||
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
||||||
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
||||||
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
||||||
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
||||||
|
|
||||||
if [ $BITS -eq 32 ]; then
|
if [ $BITS -eq 32 ]; then
|
||||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# MyGUI
|
# MyGUI
|
||||||
printf "MyGUI 3.2.2... "
|
printf "MyGUI 3.2.2... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d MyGUI ] && \
|
if [ -d MyGUI ] && \
|
||||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||||
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||||
|
@ -518,21 +513,17 @@ printf "MyGUI 3.2.2... "
|
||||||
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||||
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="_d"
|
SUFFIX="_d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# OpenAL
|
# OpenAL
|
||||||
printf "OpenAL-Soft 1.17.2... "
|
printf "OpenAL-Soft 1.17.2... "
|
||||||
{
|
{
|
||||||
|
@ -542,24 +533,18 @@ printf "OpenAL-Soft 1.17.2... "
|
||||||
rm -rf openal-soft-1.17.2-bin
|
rm -rf openal-soft-1.17.2-bin
|
||||||
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
||||||
|
|
||||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
||||||
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
||||||
|
|
||||||
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# OSG
|
# OSG
|
||||||
printf "OSG 3.4.1-scrawl... "
|
printf "OSG 3.4.1-scrawl... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d OSG ] && \
|
if [ -d OSG ] && \
|
||||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||||
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
|
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
|
||||||
|
@ -571,28 +556,21 @@ printf "OSG 3.4.1-scrawl... "
|
||||||
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
||||||
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OSG_SDK="$(real_pwd)/OSG"
|
OSG_SDK="$(real_pwd)/OSG"
|
||||||
|
|
||||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="d"
|
SUFFIX="d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
|
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
|
||||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||||
|
|
||||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
|
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
|
||||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
|
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Qt
|
# Qt
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
printf "Qt 5.7.0... "
|
printf "Qt 5.7.0... "
|
||||||
|
@ -605,71 +583,53 @@ fi
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
|
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
|
||||||
|
|
||||||
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
|
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf Qt
|
rm -rf Qt
|
||||||
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
||||||
|
|
||||||
|
|
||||||
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
||||||
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
||||||
|
|
||||||
printf -- "(Installation might take a while) "
|
printf -- "(Installation might take a while) "
|
||||||
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
||||||
|
|
||||||
mv qt-install.qs Qt/
|
mv qt-install.qs Qt/
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
printf " Cleaning up extraneous data... "
|
printf " Cleaning up extraneous data... "
|
||||||
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
|
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $QT_SDK
|
cd $QT_SDK
|
||||||
|
|
||||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="d"
|
SUFFIX="d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
else
|
else
|
||||||
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
||||||
|
|
||||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="d"
|
SUFFIX="d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||||
|
|
||||||
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||||
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
printf "SDL 2.0.7... "
|
printf "SDL 2.0.7... "
|
||||||
{
|
{
|
||||||
|
@ -679,26 +639,18 @@ printf "SDL 2.0.7... "
|
||||||
rm -rf SDL2-2.0.7
|
rm -rf SDL2-2.0.7
|
||||||
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
||||||
|
|
||||||
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
|
||||||
cd $DEPS_INSTALL/..
|
cd $DEPS_INSTALL/..
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Setting up OpenMW build..."
|
echo "Setting up OpenMW build..."
|
||||||
|
|
||||||
add_cmake_opts -DBUILD_BSATOOL=no \
|
add_cmake_opts -DBUILD_BSATOOL=no \
|
||||||
-DBUILD_ESMTOOL=no \
|
-DBUILD_ESMTOOL=no \
|
||||||
-DBUILD_MYGUI_PLUGIN=no \
|
-DBUILD_MYGUI_PLUGIN=no \
|
||||||
-DOPENMW_MP_BUILD=on
|
-DOPENMW_MP_BUILD=on
|
||||||
|
|
||||||
if [ ! -z $CI ]; then
|
if [ ! -z $CI ]; then
|
||||||
case $STEP in
|
case $STEP in
|
||||||
components )
|
components )
|
||||||
|
@ -710,7 +662,6 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_OPENMW=no \
|
-DBUILD_OPENMW=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
|
|
||||||
openmw )
|
openmw )
|
||||||
echo " Building subproject: OpenMW."
|
echo " Building subproject: OpenMW."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
|
@ -719,7 +670,6 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_OPENCS=no \
|
-DBUILD_OPENCS=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
|
|
||||||
opencs )
|
opencs )
|
||||||
echo " Building subproject: OpenCS."
|
echo " Building subproject: OpenCS."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
|
@ -728,7 +678,6 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_OPENMW=no \
|
-DBUILD_OPENMW=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
|
|
||||||
misc )
|
misc )
|
||||||
echo " Building subprojects: Misc."
|
echo " Building subprojects: Misc."
|
||||||
add_cmake_opts -DBUILD_OPENCS=no \
|
add_cmake_opts -DBUILD_OPENCS=no \
|
||||||
|
@ -736,7 +685,6 @@ if [ ! -z $CI ]; then
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# NOTE: Disable this when/if we want to run test cases
|
# NOTE: Disable this when/if we want to run test cases
|
||||||
#if [ -z $CI ]; then
|
#if [ -z $CI ]; then
|
||||||
echo "- Copying Runtime DLLs..."
|
echo "- Copying Runtime DLLs..."
|
||||||
|
@ -745,16 +693,13 @@ fi
|
||||||
TARGET="$(basename "$DLL")"
|
TARGET="$(basename "$DLL")"
|
||||||
if [[ "$DLL" == *":"* ]]; then
|
if [[ "$DLL" == *":"* ]]; then
|
||||||
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
||||||
|
|
||||||
DLL=${SPLIT[0]}
|
DLL=${SPLIT[0]}
|
||||||
TARGET=${SPLIT[1]}
|
TARGET=${SPLIT[1]}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " ${TARGET}."
|
echo " ${TARGET}."
|
||||||
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "- OSG Plugin DLLs..."
|
echo "- OSG Plugin DLLs..."
|
||||||
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
||||||
for DLL in $OSG_PLUGINS; do
|
for DLL in $OSG_PLUGINS; do
|
||||||
|
@ -762,7 +707,6 @@ fi
|
||||||
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "- Qt Platform DLLs..."
|
echo "- Qt Platform DLLs..."
|
||||||
mkdir -p ${BUILD_CONFIG}/platforms
|
mkdir -p ${BUILD_CONFIG}/platforms
|
||||||
for DLL in $QT_PLATFORMS; do
|
for DLL in $QT_PLATFORMS; do
|
||||||
|
@ -771,16 +715,13 @@ fi
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
#fi
|
#fi
|
||||||
|
|
||||||
if [ -z $VERBOSE ]; then
|
if [ -z $VERBOSE ]; then
|
||||||
printf -- "- Configuring... "
|
printf -- "- Configuring... "
|
||||||
else
|
else
|
||||||
echo "- cmake .. $CMAKE_OPTS"
|
echo "- cmake .. $CMAKE_OPTS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_cmd cmake .. $CMAKE_OPTS
|
run_cmd cmake .. $CMAKE_OPTS
|
||||||
RET=$?
|
RET=$?
|
||||||
|
|
||||||
if [ -z $VERBOSE ]; then
|
if [ -z $VERBOSE ]; then
|
||||||
if [ $RET -eq 0 ]; then
|
if [ $RET -eq 0 ]; then
|
||||||
echo Done.
|
echo Done.
|
||||||
|
@ -788,5 +729,4 @@ if [ -z $VERBOSE ]; then
|
||||||
echo Failed.
|
echo Failed.
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit $RET
|
exit $RET
|
||||||
|
|
|
@ -4,7 +4,7 @@ export CXX=clang++
|
||||||
export CC=clang
|
export CC=clang
|
||||||
|
|
||||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||||
QT_PATH=`brew --prefix $macos_qt_formula`
|
QT_PATH=`brew --prefix qt`
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
|
|
|
@ -610,10 +610,10 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
if (BUILD_OPENMW)
|
||||||
# Release builds use the debug console
|
# Release builds don't use the debug console
|
||||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||||
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
|
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS")
|
||||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Play a bit with the warning levels
|
# Play a bit with the warning levels
|
||||||
|
@ -624,7 +624,8 @@ if (WIN32)
|
||||||
# Warnings that aren't enabled normally and don't need to be enabled
|
# Warnings that aren't enabled normally and don't need to be enabled
|
||||||
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
||||||
# Not going to bother commenting them as they tend to warn on every standard library file
|
# Not going to bother commenting them as they tend to warn on every standard library file
|
||||||
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
|
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
|
||||||
|
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
|
||||||
|
|
||||||
# Warnings that are thrown on standard libraries and not OpenMW
|
# Warnings that are thrown on standard libraries and not OpenMW
|
||||||
4347 # Non-template function with same name and parameter count as template function
|
4347 # Non-template function with same name and parameter count as template function
|
||||||
|
@ -645,6 +646,7 @@ if (WIN32)
|
||||||
|
|
||||||
# caused by MyGUI
|
# caused by MyGUI
|
||||||
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
|
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
|
||||||
|
4297 # function assumed not to throw an exception but does
|
||||||
|
|
||||||
# OpenMW specific warnings
|
# OpenMW specific warnings
|
||||||
4099 # Type mismatch, declared class or struct is defined with other type
|
4099 # Type mismatch, declared class or struct is defined with other type
|
||||||
|
|
|
@ -3,7 +3,7 @@ How to contribute to OpenMW
|
||||||
|
|
||||||
Not sure what to do with all your free time? Pick out a task from here:
|
Not sure what to do with all your free time? Pick out a task from here:
|
||||||
|
|
||||||
https://bugs.openmw.org/
|
https://gitlab.com/OpenMW/openmw/issues
|
||||||
|
|
||||||
Currently, 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.
|
Currently, 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.
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
||||||
QDir::currentPath(),
|
QDir::currentPath(),
|
||||||
QString(tr("Text file (*.txt)")));
|
QString(tr("Text file (*.txt)")));
|
||||||
|
|
||||||
|
|
||||||
if (scriptFile.isEmpty())
|
if (scriptFile.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
||||||
|
runScriptAfterStartupField->setText(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::AdvancedPage::loadSettings()
|
bool Launcher::AdvancedPage::loadSettings()
|
||||||
|
@ -73,18 +72,8 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||||
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
|
||||||
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
|
||||||
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
|
||||||
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
|
||||||
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||||
|
|
||||||
// Expected values are (0, 1, 2, 3)
|
|
||||||
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
|
|
||||||
// Match the index with the option. Will default to 0 if invalid.
|
|
||||||
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
|
|
||||||
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
|
|
||||||
|
|
||||||
// Input Settings
|
// Input Settings
|
||||||
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||||
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
||||||
|
@ -94,6 +83,16 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
||||||
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
|
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
|
||||||
|
|
||||||
|
// User Interface Settings
|
||||||
|
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
||||||
|
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
||||||
|
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||||
|
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||||
|
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
|
||||||
|
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
|
||||||
|
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
|
||||||
|
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
|
||||||
|
|
||||||
// Other Settings
|
// Other Settings
|
||||||
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
|
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
|
||||||
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
|
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
|
||||||
|
@ -125,16 +124,8 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
||||||
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
||||||
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
|
||||||
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
|
||||||
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
|
||||||
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
|
||||||
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
||||||
|
|
||||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
|
||||||
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
|
||||||
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
|
||||||
|
|
||||||
// Input Settings
|
// Input Settings
|
||||||
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||||
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
||||||
|
@ -147,6 +138,15 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
|
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User Interface Settings
|
||||||
|
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
||||||
|
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
||||||
|
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||||
|
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||||
|
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
||||||
|
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
||||||
|
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
||||||
|
|
||||||
// Other Settings
|
// Other Settings
|
||||||
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
||||||
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
|
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
|
||||||
|
|
|
@ -90,19 +90,19 @@ void Launcher::MainDialog::createIcons()
|
||||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||||
graphicsButton->setIcon(QIcon::fromTheme("video-display"));
|
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
|
||||||
graphicsButton->setText(tr("Graphics"));
|
graphicsButton->setText(tr("Graphics"));
|
||||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
||||||
settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
|
settingsButton->setIcon(QIcon(":/images/preferences.png"));
|
||||||
settingsButton->setText(tr("Settings"));
|
settingsButton->setText(tr("Settings"));
|
||||||
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
||||||
advancedButton->setIcon(QIcon::fromTheme("emblem-system"));
|
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
|
||||||
advancedButton->setText(tr("Advanced"));
|
advancedButton->setText(tr("Advanced"));
|
||||||
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
|
@ -874,7 +874,6 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
|
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
|
||||||
std::string baseGameFile("Game Files:GameFile");
|
std::string baseGameFile("Game Files:GameFile");
|
||||||
std::string gameFile("");
|
|
||||||
std::time_t defaultTime = 0;
|
std::time_t defaultTime = 0;
|
||||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||||
|
|
||||||
|
@ -890,7 +889,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
||||||
multistrmap::const_iterator it = ini.begin();
|
multistrmap::const_iterator it = ini.begin();
|
||||||
for (int i=0; it != ini.end(); i++)
|
for (int i=0; it != ini.end(); i++)
|
||||||
{
|
{
|
||||||
gameFile = baseGameFile;
|
std::string gameFile = baseGameFile;
|
||||||
gameFile.append(std::to_string(i));
|
gameFile.append(std::to_string(i));
|
||||||
|
|
||||||
it = ini.find(gameFile);
|
it = ini.find(gameFile);
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
|
||||||
|
#include <components/crashcatcher/crashcatcher.hpp>
|
||||||
|
|
||||||
#include <components/fallback/validate.hpp>
|
#include <components/fallback/validate.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/nifloader.hpp>
|
#include <components/nifosg/nifloader.hpp>
|
||||||
|
@ -18,12 +21,16 @@
|
||||||
|
|
||||||
using namespace Fallback;
|
using namespace Fallback;
|
||||||
|
|
||||||
CS::Editor::Editor ()
|
CS::Editor::Editor (int argc, char **argv)
|
||||||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||||
mViewManager (mDocumentManager), mPid(""),
|
mViewManager (mDocumentManager), mPid(""),
|
||||||
mLock(), mMerge (mDocumentManager),
|
mLock(), mMerge (mDocumentManager),
|
||||||
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
|
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
|
||||||
{
|
{
|
||||||
|
// install the crash handler as soon as possible. note that the log path
|
||||||
|
// does not depend on config being read.
|
||||||
|
crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string());
|
||||||
|
|
||||||
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
|
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
|
||||||
|
|
||||||
setupDataFiles (config.first);
|
setupDataFiles (config.first);
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace CS
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Editor ();
|
Editor (int argc, char **argv);
|
||||||
~Editor ();
|
~Editor ();
|
||||||
|
|
||||||
bool makeIPCServer();
|
bool makeIPCServer();
|
||||||
|
|
|
@ -67,7 +67,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||||
|
|
||||||
CS::Editor editor;
|
CS::Editor editor(argc, argv);
|
||||||
|
|
||||||
if(!editor.makeIPCServer())
|
if(!editor.makeIPCServer())
|
||||||
{
|
{
|
||||||
|
|
|
@ -201,6 +201,9 @@ void CSMPrefs::State::declare()
|
||||||
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
|
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
|
||||||
|
|
||||||
declareCategory ("Rendering");
|
declareCategory ("Rendering");
|
||||||
|
declareInt ("framerate-limit", "FPS limit", 60).
|
||||||
|
setTooltip("Framerate limit in 3D preview windows. Zero value means \"unlimited\".").
|
||||||
|
setRange(0, 10000);
|
||||||
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
|
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
|
||||||
declareBool ("camera-ortho", "Orthographic projection for camera", false);
|
declareBool ("camera-ortho", "Orthographic projection for camera", false);
|
||||||
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).
|
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).
|
||||||
|
|
|
@ -22,7 +22,8 @@ void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
while ((pos = text.indexOf (search, pos, Qt::CaseInsensitive))!=-1)
|
Qt::CaseSensitivity caseSensitivity = mCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
|
||||||
|
while ((pos = text.indexOf (search, pos, caseSensitivity))!=-1)
|
||||||
{
|
{
|
||||||
std::ostringstream hint;
|
std::ostringstream hint;
|
||||||
hint
|
hint
|
||||||
|
@ -120,25 +121,26 @@ QString CSMTools::Search::flatten (const QString& text) const
|
||||||
return flat;
|
return flat;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMTools::Search::Search() : mType (Type_None), mValue (0), mIdColumn (0), mTypeColumn (0),
|
CSMTools::Search::Search() : mType (Type_None), mValue (0), mCase (false), mIdColumn (0), mTypeColumn (0),
|
||||||
mPaddingBefore (10), mPaddingAfter (10) {}
|
mPaddingBefore (10), mPaddingAfter (10) {}
|
||||||
|
|
||||||
CSMTools::Search::Search (Type type, const std::string& value)
|
CSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value)
|
||||||
: mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
: mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||||
{
|
{
|
||||||
if (type!=Type_Text && type!=Type_Id)
|
if (type!=Type_Text && type!=Type_Id)
|
||||||
throw std::logic_error ("Invalid search parameter (string)");
|
throw std::logic_error ("Invalid search parameter (string)");
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMTools::Search::Search (Type type, const QRegExp& value)
|
CSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value)
|
||||||
: mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
: mType (type), mRegExp (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||||
{
|
{
|
||||||
|
mRegExp.setCaseSensitivity(mCase ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||||
if (type!=Type_TextRegEx && type!=Type_IdRegEx)
|
if (type!=Type_TextRegEx && type!=Type_IdRegEx)
|
||||||
throw std::logic_error ("Invalid search parameter (RegExp)");
|
throw std::logic_error ("Invalid search parameter (RegExp)");
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMTools::Search::Search (Type type, int value)
|
CSMTools::Search::Search (Type type, bool caseSensitive, int value)
|
||||||
: mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
: mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||||
{
|
{
|
||||||
if (type!=Type_RecordState)
|
if (type!=Type_RecordState)
|
||||||
throw std::logic_error ("invalid search parameter (int)");
|
throw std::logic_error ("invalid search parameter (int)");
|
||||||
|
|
|
@ -43,6 +43,7 @@ namespace CSMTools
|
||||||
std::string mText;
|
std::string mText;
|
||||||
QRegExp mRegExp;
|
QRegExp mRegExp;
|
||||||
int mValue;
|
int mValue;
|
||||||
|
bool mCase;
|
||||||
std::set<int> mColumns;
|
std::set<int> mColumns;
|
||||||
int mIdColumn;
|
int mIdColumn;
|
||||||
int mTypeColumn;
|
int mTypeColumn;
|
||||||
|
@ -67,11 +68,11 @@ namespace CSMTools
|
||||||
|
|
||||||
Search();
|
Search();
|
||||||
|
|
||||||
Search (Type type, const std::string& value);
|
Search (Type type, bool caseSensitive, const std::string& value);
|
||||||
|
|
||||||
Search (Type type, const QRegExp& value);
|
Search (Type type, bool caseSensitive, const QRegExp& value);
|
||||||
|
|
||||||
Search (Type type, int value);
|
Search (Type type, bool caseSensitive, int value);
|
||||||
|
|
||||||
// Configure search for the specified model.
|
// Configure search for the specified model.
|
||||||
void configure (const CSMWorld::IdTableBase *model);
|
void configure (const CSMWorld::IdTableBase *model);
|
||||||
|
|
|
@ -136,7 +136,7 @@ namespace CSMWorld
|
||||||
struct VarTypeColumn : public Column<ESXRecordT>
|
struct VarTypeColumn : public Column<ESXRecordT>
|
||||||
{
|
{
|
||||||
VarTypeColumn (ColumnBase::Display display)
|
VarTypeColumn (ColumnBase::Display display)
|
||||||
: Column<ESXRecordT> (Columns::ColumnId_ValueType, display)
|
: Column<ESXRecordT> (Columns::ColumnId_ValueType, display, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
@ -161,7 +161,7 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT>
|
template<typename ESXRecordT>
|
||||||
struct VarValueColumn : public Column<ESXRecordT>
|
struct VarValueColumn : public Column<ESXRecordT>
|
||||||
{
|
{
|
||||||
VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var) {}
|
VarValueColumn() : Column<ESXRecordT> (Columns::ColumnId_Value, ColumnBase::Display_Var, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh) {}
|
||||||
|
|
||||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,15 +84,28 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
|
||||||
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
|
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
|
||||||
{
|
{
|
||||||
mIdCollection->setData (index.row(), index.column(), value);
|
mIdCollection->setData (index.row(), index.column(), value);
|
||||||
emit dataChanged(index, index);
|
|
||||||
|
|
||||||
// Modifying a value can also change the Modified status of a record.
|
|
||||||
int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);
|
int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);
|
||||||
if (stateColumn != -1)
|
if (stateColumn != -1)
|
||||||
{
|
{
|
||||||
|
if (index.column() == stateColumn)
|
||||||
|
{
|
||||||
|
// modifying the state column can modify other values. we need to tell
|
||||||
|
// views that the whole row has changed.
|
||||||
|
|
||||||
|
emit dataChanged(this->index(index.row(), 0),
|
||||||
|
this->index(index.row(), columnCount(index.parent())));
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
|
||||||
|
// Modifying a value can also change the Modified status of a record.
|
||||||
QModelIndex stateIndex = this->index(index.row(), stateColumn);
|
QModelIndex stateIndex = this->index(index.row(), stateColumn);
|
||||||
emit dataChanged(stateIndex, stateIndex);
|
emit dataChanged(stateIndex, stateIndex);
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,15 +101,39 @@ void CSVDoc::View::setupFileMenu()
|
||||||
file->addAction(exit);
|
file->addAction(exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void updateUndoRedoAction(QAction *action, const std::string &settingsKey)
|
||||||
|
{
|
||||||
|
QKeySequence seq;
|
||||||
|
CSMPrefs::State::get().getShortcutManager().getSequence(settingsKey, seq);
|
||||||
|
action->setShortcut(seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::undoActionChanged()
|
||||||
|
{
|
||||||
|
updateUndoRedoAction(mUndo, "document-edit-undo");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::redoActionChanged()
|
||||||
|
{
|
||||||
|
updateUndoRedoAction(mRedo, "document-edit-redo");
|
||||||
|
}
|
||||||
|
|
||||||
void CSVDoc::View::setupEditMenu()
|
void CSVDoc::View::setupEditMenu()
|
||||||
{
|
{
|
||||||
QMenu *edit = menuBar()->addMenu (tr ("Edit"));
|
QMenu *edit = menuBar()->addMenu (tr ("Edit"));
|
||||||
|
|
||||||
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
|
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
|
||||||
setupShortcut("document-edit-undo", mUndo);
|
setupShortcut("document-edit-undo", mUndo);
|
||||||
|
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
|
||||||
edit->addAction (mUndo);
|
edit->addAction (mUndo);
|
||||||
|
|
||||||
mRedo= mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
|
mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo"));
|
||||||
|
connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ()));
|
||||||
setupShortcut("document-edit-redo", mRedo);
|
setupShortcut("document-edit-redo", mRedo);
|
||||||
edit->addAction (mRedo);
|
edit->addAction (mRedo);
|
||||||
|
|
||||||
|
@ -622,6 +646,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
|
||||||
}
|
}
|
||||||
assert(view);
|
assert(view);
|
||||||
view->setParent(this);
|
view->setParent(this);
|
||||||
|
view->setEditLock (mDocument->getState() & CSMDoc::State_Locked);
|
||||||
mSubViews.append(view); // only after assert
|
mSubViews.append(view); // only after assert
|
||||||
|
|
||||||
int minWidth = windows["minimum-width"].toInt();
|
int minWidth = windows["minimum-width"].toInt();
|
||||||
|
|
|
@ -152,6 +152,10 @@ namespace CSVDoc
|
||||||
|
|
||||||
void settingChanged (const CSMPrefs::Setting *setting);
|
void settingChanged (const CSMPrefs::Setting *setting);
|
||||||
|
|
||||||
|
void undoActionChanged();
|
||||||
|
|
||||||
|
void redoActionChanged();
|
||||||
|
|
||||||
void newView();
|
void newView();
|
||||||
|
|
||||||
void save();
|
void save();
|
||||||
|
|
|
@ -102,6 +102,14 @@ void CSVRender::Object::update()
|
||||||
if (recordType == CSMWorld::UniversalId::Type_Light)
|
if (recordType == CSMWorld::UniversalId::Type_Light)
|
||||||
{
|
{
|
||||||
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
|
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
|
||||||
|
if (model.empty())
|
||||||
|
model = "marker_light.nif";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
|
||||||
|
{
|
||||||
|
if (model.empty())
|
||||||
|
model = "marker_creature.nif";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.empty())
|
if (model.empty())
|
||||||
|
@ -372,10 +380,10 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
|
||||||
indices[j] -= VertexCount;
|
indices[j] -= VertexCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t offset = i * IndicesPerSegment;
|
size_t elementOffset = i * IndicesPerSegment;
|
||||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
||||||
{
|
{
|
||||||
primitives->setElement(offset++, indices[IndexPattern[j]]);
|
primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,6 +151,9 @@ CompositeViewer::CompositeViewer()
|
||||||
|
|
||||||
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
|
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
|
||||||
mTimer.start( 10 );
|
mTimer.start( 10 );
|
||||||
|
|
||||||
|
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
|
||||||
|
setRunMaxFrameRate(frameRateLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositeViewer &CompositeViewer::get()
|
CompositeViewer &CompositeViewer::get()
|
||||||
|
@ -168,6 +171,12 @@ void CompositeViewer::update()
|
||||||
|
|
||||||
mSimulationTime += dt;
|
mSimulationTime += dt;
|
||||||
frame(mSimulationTime);
|
frame(mSimulationTime);
|
||||||
|
|
||||||
|
double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0;
|
||||||
|
if (dt < minFrameTime)
|
||||||
|
{
|
||||||
|
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-dt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------
|
// ---------------------------------------------------
|
||||||
|
@ -376,6 +385,10 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
|
||||||
{
|
{
|
||||||
mOrbitCamControl->setConstRoll(setting->isTrue());
|
mOrbitCamControl->setConstRoll(setting->isTrue());
|
||||||
}
|
}
|
||||||
|
else if (*setting=="Rendering/framerate-limit")
|
||||||
|
{
|
||||||
|
CompositeViewer::get().setRunMaxFrameRate(setting->toInt());
|
||||||
|
}
|
||||||
else if (*setting=="Rendering/camera-fov" ||
|
else if (*setting=="Rendering/camera-fov" ||
|
||||||
*setting=="Rendering/camera-ortho" ||
|
*setting=="Rendering/camera-ortho" ||
|
||||||
*setting=="Rendering/camera-ortho-size")
|
*setting=="Rendering/camera-ortho-size")
|
||||||
|
|
|
@ -651,7 +651,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
|
||||||
|
|
||||||
if (mDragMode == InteractionType_PrimaryEdit)
|
if (mDragMode == InteractionType_PrimaryEdit)
|
||||||
{
|
{
|
||||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
|
|
||||||
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
|
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ void CSVTools::SearchBox::updateSearchButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVTools::SearchBox::SearchBox (QWidget *parent)
|
CSVTools::SearchBox::SearchBox (QWidget *parent)
|
||||||
: QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All")
|
: QWidget (parent), mSearch (tr("Search")), mSearchEnabled (false), mReplace (tr("Replace All"))
|
||||||
{
|
{
|
||||||
mLayout = new QGridLayout (this);
|
mLayout = new QGridLayout (this);
|
||||||
|
|
||||||
|
@ -48,29 +48,26 @@ CSVTools::SearchBox::SearchBox (QWidget *parent)
|
||||||
++iter)
|
++iter)
|
||||||
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
|
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
|
||||||
|
|
||||||
mMode.addItem ("Text");
|
mMode.addItem (tr("Text"));
|
||||||
mMode.addItem ("Text (RegEx)");
|
mMode.addItem (tr("Text (RegEx)"));
|
||||||
mMode.addItem ("ID");
|
mMode.addItem (tr("ID"));
|
||||||
mMode.addItem ("ID (RegEx)");
|
mMode.addItem (tr("ID (RegEx)"));
|
||||||
mMode.addItem ("Record State");
|
mMode.addItem (tr("Record State"));
|
||||||
|
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
||||||
mLayout->addWidget (&mMode, 0, 0);
|
mLayout->addWidget (&mMode, 0, 0);
|
||||||
|
|
||||||
mLayout->addWidget (&mSearch, 0, 3);
|
connect (&mText, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
|
||||||
|
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
|
||||||
mInput.insertWidget (0, &mText);
|
mInput.insertWidget (0, &mText);
|
||||||
mInput.insertWidget (1, &mRecordState);
|
|
||||||
|
|
||||||
|
mInput.insertWidget (1, &mRecordState);
|
||||||
mLayout->addWidget (&mInput, 0, 1);
|
mLayout->addWidget (&mInput, 0, 1);
|
||||||
|
|
||||||
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
mCaseSensitive.setText (tr ("Case"));
|
||||||
|
mLayout->addWidget (&mCaseSensitive, 0, 2);
|
||||||
connect (&mText, SIGNAL (textChanged (const QString&)),
|
|
||||||
this, SLOT (textChanged (const QString&)));
|
|
||||||
|
|
||||||
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
|
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
|
||||||
|
mLayout->addWidget (&mSearch, 0, 3);
|
||||||
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
|
|
||||||
|
|
||||||
// replace panel
|
// replace panel
|
||||||
mReplaceInput.insertWidget (0, &mReplaceText);
|
mReplaceInput.insertWidget (0, &mReplaceText);
|
||||||
|
@ -103,22 +100,23 @@ void CSVTools::SearchBox::setSearchMode (bool enabled)
|
||||||
CSMTools::Search CSVTools::SearchBox::getSearch() const
|
CSMTools::Search CSVTools::SearchBox::getSearch() const
|
||||||
{
|
{
|
||||||
CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());
|
CSMTools::Search::Type type = static_cast<CSMTools::Search::Type> (mMode.currentIndex());
|
||||||
|
bool caseSensitive = mCaseSensitive.isChecked();
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case CSMTools::Search::Type_Text:
|
case CSMTools::Search::Type_Text:
|
||||||
case CSMTools::Search::Type_Id:
|
case CSMTools::Search::Type_Id:
|
||||||
|
|
||||||
return CSMTools::Search (type, std::string (mText.text().toUtf8().data()));
|
return CSMTools::Search (type, caseSensitive, std::string (mText.text().toUtf8().data()));
|
||||||
|
|
||||||
case CSMTools::Search::Type_TextRegEx:
|
case CSMTools::Search::Type_TextRegEx:
|
||||||
case CSMTools::Search::Type_IdRegEx:
|
case CSMTools::Search::Type_IdRegEx:
|
||||||
|
|
||||||
return CSMTools::Search (type, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive));
|
return CSMTools::Search (type, caseSensitive, QRegExp (mText.text().toUtf8().data(), Qt::CaseInsensitive));
|
||||||
|
|
||||||
case CSMTools::Search::Type_RecordState:
|
case CSMTools::Search::Type_RecordState:
|
||||||
|
|
||||||
return CSMTools::Search (type, mRecordState.currentIndex());
|
return CSMTools::Search (type, caseSensitive, mRecordState.currentIndex());
|
||||||
|
|
||||||
case CSMTools::Search::Type_None:
|
case CSMTools::Search::Type_None:
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
@ -24,6 +25,7 @@ namespace CSVTools
|
||||||
QStackedWidget mInput;
|
QStackedWidget mInput;
|
||||||
QLineEdit mText;
|
QLineEdit mText;
|
||||||
QComboBox mRecordState;
|
QComboBox mRecordState;
|
||||||
|
QCheckBox mCaseSensitive;
|
||||||
QPushButton mSearch;
|
QPushButton mSearch;
|
||||||
QGridLayout *mLayout;
|
QGridLayout *mLayout;
|
||||||
QComboBox mMode;
|
QComboBox mMode;
|
||||||
|
|
|
@ -32,7 +32,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps ()
|
||||||
|
|
||||||
while (it != mIcons.end())
|
while (it != mIcons.end())
|
||||||
{
|
{
|
||||||
mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) );
|
mPixmaps.push_back (std::make_pair (it->mValue, it->mIcon.pixmap (mIconSize) ) );
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,9 +142,23 @@ void CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *set
|
||||||
|
|
||||||
void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename)
|
void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename)
|
||||||
{
|
{
|
||||||
mIcons.push_back (std::make_pair(enumValue, QIcon(iconFilename)));
|
|
||||||
EnumDelegateFactory::add(enumValue, enumName);
|
EnumDelegateFactory::add(enumValue, enumName);
|
||||||
|
|
||||||
|
Icon icon;
|
||||||
|
icon.mValue = enumValue;
|
||||||
|
icon.mName = enumName;
|
||||||
|
icon.mIcon = QIcon(iconFilename);
|
||||||
|
|
||||||
|
for (auto it=mIcons.begin(); it!=mIcons.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->mName > enumName)
|
||||||
|
{
|
||||||
|
mIcons.insert(it, icon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mIcons.push_back(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (
|
CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (
|
||||||
|
|
|
@ -11,11 +11,18 @@ namespace CSMPrefs
|
||||||
|
|
||||||
namespace CSVWorld
|
namespace CSVWorld
|
||||||
{
|
{
|
||||||
|
struct Icon
|
||||||
|
{
|
||||||
|
int mValue;
|
||||||
|
QIcon mIcon;
|
||||||
|
QString mName;
|
||||||
|
};
|
||||||
|
|
||||||
class DataDisplayDelegate : public EnumDelegate
|
class DataDisplayDelegate : public EnumDelegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef std::vector < std::pair < int, QIcon > > IconList;
|
typedef std::vector<Icon> IconList;
|
||||||
typedef std::vector<std::pair<int, QString> > ValueList;
|
typedef std::vector<std::pair<int, QString> > ValueList;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -110,7 +110,11 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
|
||||||
int valueIndex = getValueIndex(index);
|
int valueIndex = getValueIndex(index);
|
||||||
if (valueIndex != -1)
|
if (valueIndex != -1)
|
||||||
{
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
|
||||||
|
QStyleOptionViewItem itemOption(option);
|
||||||
|
#else
|
||||||
QStyleOptionViewItemV4 itemOption(option);
|
QStyleOptionViewItemV4 itemOption(option);
|
||||||
|
#endif
|
||||||
itemOption.text = mValues.at(valueIndex).second;
|
itemOption.text = mValues.at(valueIndex).second;
|
||||||
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
|
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
|
||||||
}
|
}
|
||||||
|
@ -171,5 +175,16 @@ CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
|
||||||
|
|
||||||
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)
|
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)
|
||||||
{
|
{
|
||||||
mValues.push_back (std::make_pair (value, name));
|
auto pair = std::make_pair (value, name);
|
||||||
|
|
||||||
|
for (auto it=mValues.begin(); it!=mValues.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second > name)
|
||||||
|
{
|
||||||
|
mValues.insert(it, pair);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mValues.push_back(std::make_pair (value, name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,10 @@ if (ANDROID)
|
||||||
set(GAME ${GAME} android_main.c)
|
set(GAME ${GAME} android_main.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT ANDROID)
|
|
||||||
set(GAME ${GAME} crashcatcher.cpp)
|
|
||||||
endif()
|
|
||||||
set(GAME_HEADER
|
set(GAME_HEADER
|
||||||
engine.hpp
|
engine.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||||
|
|
||||||
add_openmw_dir (mwrender
|
add_openmw_dir (mwrender
|
||||||
|
@ -83,9 +81,9 @@ add_openmw_dir (mwclass
|
||||||
add_openmw_dir (mwmechanics
|
add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
||||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||||
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
||||||
character actors objects aistate coordinateconverter trading aiface weaponpriority spellpriority
|
character actors objects aistate coordinateconverter trading weaponpriority spellpriority
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwstate
|
add_openmw_dir (mwstate
|
||||||
|
|
|
@ -381,33 +381,24 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
setWindowIcon();
|
setWindowIcon();
|
||||||
|
|
||||||
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
||||||
int redSize;
|
|
||||||
int greenSize;
|
|
||||||
int blueSize;
|
|
||||||
int depthSize;
|
|
||||||
int stencilSize;
|
|
||||||
int doubleBuffer;
|
|
||||||
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redSize);
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenSize);
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blueSize);
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize);
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencilSize);
|
|
||||||
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doubleBuffer);
|
|
||||||
|
|
||||||
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
||||||
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
||||||
traits->red = redSize;
|
|
||||||
traits->green = greenSize;
|
|
||||||
traits->blue = blueSize;
|
|
||||||
traits->depth = depthSize;
|
|
||||||
traits->stencil = stencilSize;
|
|
||||||
traits->doubleBuffer = doubleBuffer;
|
|
||||||
traits->windowName = SDL_GetWindowTitle(mWindow);
|
traits->windowName = SDL_GetWindowTitle(mWindow);
|
||||||
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
||||||
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
||||||
traits->vsync = vsync;
|
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
|
||||||
|
// Here goes kcat's quote:
|
||||||
|
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
|
||||||
|
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
|
||||||
|
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
|
||||||
|
traits->red = 8;
|
||||||
|
traits->green = 8;
|
||||||
|
traits->blue = 8;
|
||||||
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
||||||
|
traits->depth = 24;
|
||||||
|
traits->stencil = 8;
|
||||||
|
traits->vsync = vsync;
|
||||||
|
traits->doubleBuffer = true;
|
||||||
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
||||||
|
|
||||||
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
||||||
|
@ -646,28 +637,19 @@ void OMW::Engine::go()
|
||||||
|
|
||||||
std::cout << "OSG version: " << osgGetVersion() << std::endl;
|
std::cout << "OSG version: " << osgGetVersion() << std::endl;
|
||||||
|
|
||||||
mViewer = new osgViewer::Viewer;
|
// Load settings
|
||||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
|
||||||
|
|
||||||
osg::ref_ptr<osgViewer::StatsHandler> statshandler = new osgViewer::StatsHandler;
|
|
||||||
statshandler->setKeyEventTogglesOnScreenStats(osgGA::GUIEventAdapter::KEY_F3);
|
|
||||||
|
|
||||||
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
|
||||||
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
|
|
||||||
statshandler->addUserStatsLine("Mechanics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
|
||||||
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
|
|
||||||
statshandler->addUserStatsLine("Physics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
|
||||||
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
|
|
||||||
|
|
||||||
mViewer->addEventHandler(statshandler);
|
|
||||||
|
|
||||||
mViewer->addEventHandler(new Resource::StatsHandler);
|
|
||||||
|
|
||||||
Settings::Manager settings;
|
Settings::Manager settings;
|
||||||
std::string settingspath;
|
std::string settingspath;
|
||||||
|
|
||||||
settingspath = loadSettings (settings);
|
settingspath = loadSettings (settings);
|
||||||
|
|
||||||
|
// Create encoder
|
||||||
|
ToUTF8::Utf8Encoder encoder (mEncoding);
|
||||||
|
mEncoder = &encoder;
|
||||||
|
|
||||||
|
// Setup viewer
|
||||||
|
mViewer = new osgViewer::Viewer;
|
||||||
|
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||||
|
|
||||||
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
|
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
|
||||||
Settings::Manager::getString("screenshot format", "General"));
|
Settings::Manager::getString("screenshot format", "General"));
|
||||||
|
|
||||||
|
@ -677,12 +659,24 @@ void OMW::Engine::go()
|
||||||
|
|
||||||
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||||
|
|
||||||
// Create encoder
|
|
||||||
ToUTF8::Utf8Encoder encoder (mEncoding);
|
|
||||||
mEncoder = &encoder;
|
|
||||||
|
|
||||||
prepareEngine (settings);
|
prepareEngine (settings);
|
||||||
|
|
||||||
|
// Setup profiler
|
||||||
|
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler;
|
||||||
|
|
||||||
|
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
|
||||||
|
statshandler->addUserStatsLine("Mechanics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
|
||||||
|
statshandler->addUserStatsLine("Physics", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
|
||||||
|
|
||||||
|
mViewer->addEventHandler(statshandler);
|
||||||
|
|
||||||
|
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
|
||||||
|
mViewer->addEventHandler(resourceshandler);
|
||||||
|
|
||||||
|
// Start the game
|
||||||
if (!mSaveGameFile.empty())
|
if (!mSaveGameFile.empty())
|
||||||
{
|
{
|
||||||
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <components/version/version.hpp>
|
#include <components/version/version.hpp>
|
||||||
|
#include <components/crashcatcher/crashcatcher.hpp>
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
#include <components/files/escape.hpp>
|
#include <components/files/escape.hpp>
|
||||||
#include <components/fallback/validate.hpp>
|
#include <components/fallback/validate.hpp>
|
||||||
|
@ -24,18 +25,6 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix))
|
|
||||||
#define USE_CRASH_CATCHER 1
|
|
||||||
#else
|
|
||||||
#define USE_CRASH_CATCHER 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_CRASH_CATCHER
|
|
||||||
#include <csignal>
|
|
||||||
extern int cc_install_handlers(int argc, char **argv, int num_signals, int *sigs, const char *logfile, int (*user_info)(char*, char*));
|
|
||||||
extern int is_debugger_attached(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||||
*/
|
*/
|
||||||
|
@ -339,18 +328,7 @@ int main(int argc, char**argv)
|
||||||
std::cerr.rdbuf (&cerrsb);
|
std::cerr.rdbuf (&cerrsb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
|
||||||
#if USE_CRASH_CATCHER
|
|
||||||
// Unix crash catcher
|
|
||||||
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
|
|
||||||
{
|
|
||||||
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
|
|
||||||
cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL);
|
|
||||||
std::cout << "Installing crash catcher" << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
std::cout << "Running in a debugger, not installing crash catcher" << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
|
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
|
||||||
|
|
|
@ -225,9 +225,12 @@ namespace MWBase
|
||||||
/// Resurrects the player if necessary
|
/// Resurrects the player if necessary
|
||||||
virtual void keepPlayerAlive() = 0;
|
virtual void keepPlayerAlive() = 0;
|
||||||
|
|
||||||
|
virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const = 0;
|
||||||
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
|
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
|
||||||
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
|
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
|
||||||
|
|
||||||
|
virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;
|
||||||
|
|
||||||
/// Check if the target actor was detected by an observer
|
/// Check if the target actor was detected by an observer
|
||||||
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
||||||
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;
|
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;
|
||||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void endGame() = 0;
|
virtual void endGame() = 0;
|
||||||
|
|
||||||
|
virtual void resumeGame() = 0;
|
||||||
|
|
||||||
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
||||||
|
|
||||||
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
||||||
|
|
|
@ -146,7 +146,7 @@ namespace MWBase
|
||||||
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
virtual void useItem(const MWWorld::Ptr& item) = 0;
|
virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
|
||||||
|
|
||||||
virtual void updateSpellWindow() = 0;
|
virtual void updateSpellWindow() = 0;
|
||||||
|
|
||||||
|
|
|
@ -297,7 +297,7 @@ namespace MWBase
|
||||||
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
||||||
/// doPhysics.
|
/// doPhysics.
|
||||||
|
|
||||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0;
|
||||||
///< cast a Ray and return true if there is an object in the ray path.
|
///< cast a Ray and return true if there is an object in the ray path.
|
||||||
|
|
||||||
virtual bool toggleCollisionMode() = 0;
|
virtual bool toggleCollisionMode() = 0;
|
||||||
|
@ -487,7 +487,7 @@ namespace MWBase
|
||||||
*/
|
*/
|
||||||
virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;
|
virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;
|
||||||
|
|
||||||
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
|
virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;
|
||||||
|
|
||||||
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
|
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
|
||||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
||||||
|
|
|
@ -124,10 +124,9 @@ namespace MWClass
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr) const
|
|
||||||
{
|
{
|
||||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionAlchemy());
|
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionAlchemy(force));
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const
|
MWWorld::Ptr Apparatus::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const
|
||||||
|
|
|
@ -50,8 +50,7 @@ namespace MWClass
|
||||||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -354,9 +354,9 @@ namespace MWClass
|
||||||
return std::make_pair(1,"");
|
return std::make_pair(1,"");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Armor::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Armor::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,7 @@ namespace MWClass
|
||||||
///< 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
|
///< 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
|
/// Second item in the pair specifies the error message
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -162,7 +162,7 @@ namespace MWClass
|
||||||
return record->mId;
|
return record->mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Book::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Book::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr));
|
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRead(ptr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace MWClass
|
||||||
virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const;
|
virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const;
|
||||||
///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it.
|
///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it.
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) const;
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -243,9 +243,9 @@ namespace MWClass
|
||||||
return std::make_pair (1, "");
|
return std::make_pair (1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Clothing::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Clothing::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,7 @@ namespace MWClass
|
||||||
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
|
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
|
||||||
/// Second item in the pair specifies the error message
|
/// Second item in the pair specifies the error message
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -519,6 +519,9 @@ namespace MWClass
|
||||||
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
const GMST& gmst = getGmst();
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
||||||
|
@ -611,7 +614,7 @@ namespace MWClass
|
||||||
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
|
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
||||||
return static_cast<float>(stats.getAttribute(0).getModified() * 5);
|
return static_cast<float>(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Creature::getServices(const MWWorld::ConstPtr &actor) const
|
int Creature::getServices(const MWWorld::ConstPtr &actor) const
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Ingredient::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Ingredient::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionEat (ptr));
|
std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionEat (ptr));
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,7 @@ namespace MWClass
|
||||||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
static void registerSelf();
|
static void registerSelf();
|
||||||
|
|
|
@ -186,9 +186,9 @@ namespace MWClass
|
||||||
return Class::showsInInventory(ptr);
|
return Class::showsInInventory(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Light::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Light::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -55,8 +55,7 @@ namespace MWClass
|
||||||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const;
|
virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const;
|
||||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Lockpick::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Lockpick::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
||||||
|
|
||||||
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -226,7 +226,7 @@ namespace MWClass
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Miscellaneous::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul()))
|
if (ptr.getCellRef().getSoul().empty() || !MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().search(ptr.getCellRef().getSoul()))
|
||||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());
|
return std::shared_ptr<MWWorld::Action>(new MWWorld::NullAction());
|
||||||
|
|
|
@ -49,8 +49,7 @@ namespace MWClass
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual float getWeight (const MWWorld::ConstPtr& ptr) const;
|
virtual float getWeight (const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
|
@ -773,22 +773,24 @@ namespace MWClass
|
||||||
float x = damage / (damage + getArmorRating(ptr));
|
float x = damage / (damage + getArmorRating(ptr));
|
||||||
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
|
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
|
||||||
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
|
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
|
||||||
if (damage < 1)
|
damage = std::max(1.f, damage);
|
||||||
damage = 1;
|
damageDiff = std::max(1, damageDiff);
|
||||||
|
|
||||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||||
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
||||||
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
||||||
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
|
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
|
||||||
|
{
|
||||||
|
if (attacker.isEmpty() || (!attacker.isEmpty() && !(object.isEmpty() && !attacker.getClass().isNpc()))) // Unarmed creature attacks don't affect armor condition
|
||||||
{
|
{
|
||||||
int armorhealth = armor.getClass().getItemHealth(armor);
|
int armorhealth = armor.getClass().getItemHealth(armor);
|
||||||
armorhealth -= std::min(std::max(1, damageDiff),
|
armorhealth -= std::min(damageDiff, armorhealth);
|
||||||
armorhealth);
|
|
||||||
armor.getCellRef().setCharge(armorhealth);
|
armor.getCellRef().setCharge(armorhealth);
|
||||||
|
|
||||||
// Armor broken? unequip it
|
// Armor broken? unequip it
|
||||||
if (armorhealth == 0)
|
if (armorhealth == 0)
|
||||||
armor = *inv.unequipItem(armor, ptr);
|
armor = *inv.unequipItem(armor, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
if (ptr == MWMechanics::getPlayer())
|
if (ptr == MWMechanics::getPlayer())
|
||||||
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
|
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
|
||||||
|
@ -923,6 +925,10 @@ namespace MWClass
|
||||||
|
|
||||||
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
|
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
const GMST& gmst = getGmst();
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
|
@ -931,8 +937,8 @@ namespace MWClass
|
||||||
|
|
||||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||||
|
|
||||||
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||||
|
|
||||||
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
||||||
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
|
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
|
||||||
|
@ -1073,7 +1079,7 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
||||||
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat();
|
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat();
|
||||||
return stats.getAttribute(0).getModified()*fEncumbranceStrMult;
|
return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
|
float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
|
||||||
|
|
|
@ -139,7 +139,7 @@ namespace MWClass
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Potion::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Potion::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
MWWorld::LiveCellRef<ESM::Potion> *ref =
|
MWWorld::LiveCellRef<ESM::Potion> *ref =
|
||||||
ptr.get<ESM::Potion>();
|
ptr.get<ESM::Potion>();
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace MWClass
|
||||||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr) const;
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
static void registerSelf();
|
static void registerSelf();
|
||||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Probe::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Probe::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
||||||
|
|
||||||
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
virtual std::pair<int, std::string> canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const;
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -149,9 +149,9 @@ namespace MWClass
|
||||||
return MWWorld::Ptr(cell.insert(ref), &cell);
|
return MWWorld::Ptr(cell.insert(ref), &cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Repair::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr));
|
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionRepair(ptr, force));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const
|
bool Repair::canSell (const MWWorld::ConstPtr& item, int npcServices) const
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false)
|
||||||
const;
|
const;
|
||||||
///< Generate action for using via inventory menu (default implementation: return a
|
///< Generate action for using via inventory menu (default implementation: return a
|
||||||
/// null action).
|
/// null action).
|
||||||
|
|
|
@ -401,9 +401,9 @@ namespace MWClass
|
||||||
return std::make_pair(1, "");
|
return std::make_pair(1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Weapon::use (const MWWorld::Ptr& ptr) const
|
std::shared_ptr<MWWorld::Action> Weapon::use (const MWWorld::Ptr& ptr, bool force) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionEquip(ptr, force));
|
||||||
|
|
||||||
action->setSound(getUpSoundId(ptr));
|
action->setSound(getUpSoundId(ptr));
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,7 @@ namespace MWClass
|
||||||
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
|
///< Return 0 if player cannot equip item. 1 if can equip. 2 if it's twohanded weapon. 3 if twohanded weapon conflicts with that.
|
||||||
/// Second item in the pair specifies the error message
|
/// Second item in the pair specifies the error message
|
||||||
|
|
||||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr)
|
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||||
const;
|
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||||
|
|
|
@ -629,6 +629,9 @@ namespace MWGui
|
||||||
|
|
||||||
void DialogueWindow::onTopicActivated(const std::string &topicId)
|
void DialogueWindow::onTopicActivated(const std::string &topicId)
|
||||||
{
|
{
|
||||||
|
if (mGoodbye)
|
||||||
|
return;
|
||||||
|
|
||||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());
|
MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());
|
||||||
updateTopics();
|
updateTopics();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,16 @@ namespace MWGui
|
||||||
|
|
||||||
boost::algorithm::replace_all(mText, "\r", "");
|
boost::algorithm::replace_all(mText, "\r", "");
|
||||||
|
|
||||||
// vanilla game does not show any text after last <BR> tag.
|
// vanilla game does not show any text after the last EOL tag.
|
||||||
const std::string lowerText = Misc::StringUtils::lowerCase(mText);
|
const std::string lowerText = Misc::StringUtils::lowerCase(mText);
|
||||||
int index = lowerText.rfind("<br>");
|
int brIndex = lowerText.rfind("<br>");
|
||||||
if (index == -1)
|
int pIndex = lowerText.rfind("<p>");
|
||||||
|
if (brIndex == pIndex)
|
||||||
mText = "";
|
mText = "";
|
||||||
|
else if (brIndex > pIndex)
|
||||||
|
mText = mText.substr(0, brIndex+4);
|
||||||
else
|
else
|
||||||
mText = mText.substr(0, index+4);
|
mText = mText.substr(0, pIndex+3);
|
||||||
|
|
||||||
registerTag("br", Event_BrTag);
|
registerTag("br", Event_BrTag);
|
||||||
registerTag("p", Event_PTag);
|
registerTag("p", Event_PTag);
|
||||||
|
|
|
@ -158,7 +158,9 @@ namespace MWGui
|
||||||
|
|
||||||
getWidget(mCrosshair, "Crosshair");
|
getWidget(mCrosshair, "Crosshair");
|
||||||
|
|
||||||
LocalMapBase::init(mMinimap, mCompass, Settings::Manager::getInt("local map hud widget size", "Map"), Settings::Manager::getInt("local map cell distance", "Map"));
|
int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map"));
|
||||||
|
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
|
||||||
|
LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance);
|
||||||
|
|
||||||
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
|
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
|
||||||
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);
|
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/action.hpp"
|
#include "../mwworld/actionequip.hpp"
|
||||||
#include "../mwscript/interpretercontext.hpp"
|
#include "../mwscript/interpretercontext.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
@ -474,7 +474,7 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
|
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryWindow::useItem(const MWWorld::Ptr &ptr)
|
void InventoryWindow::useItem(const MWWorld::Ptr &ptr, bool force)
|
||||||
{
|
{
|
||||||
const std::string& script = ptr.getClass().getScript(ptr);
|
const std::string& script = ptr.getClass().getScript(ptr);
|
||||||
|
|
||||||
|
@ -482,8 +482,18 @@ namespace MWGui
|
||||||
|
|
||||||
// early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case
|
// early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that case
|
||||||
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
|
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
|
||||||
|
{
|
||||||
|
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||||
|
updateItemView();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force)
|
||||||
{
|
{
|
||||||
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||||
|
|
||||||
if (canEquip.first == 0)
|
if (canEquip.first == 0)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||||
|
@ -491,6 +501,7 @@ namespace MWGui
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the item has a script, set its OnPcEquip to 1
|
// If the item has a script, set its OnPcEquip to 1
|
||||||
if (!script.empty()
|
if (!script.empty()
|
||||||
|
@ -512,9 +523,8 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
|
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
|
||||||
{
|
{
|
||||||
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr);
|
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
||||||
|
action->execute(player);
|
||||||
action->execute (player);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mSkippedToEquip = ptr;
|
mSkippedToEquip = ptr;
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace MWGui
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void useItem(const MWWorld::Ptr& ptr);
|
void useItem(const MWWorld::Ptr& ptr, bool force=false);
|
||||||
|
|
||||||
void setGuiMode(GuiMode mode);
|
void setGuiMode(GuiMode mode);
|
||||||
|
|
||||||
|
|
|
@ -679,7 +679,9 @@ namespace MWGui
|
||||||
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||||
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
|
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
|
||||||
|
|
||||||
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, Settings::Manager::getInt("local map widget size", "Map"), Settings::Manager::getInt("local map cell distance", "Map"));
|
int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
|
||||||
|
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
|
||||||
|
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance);
|
||||||
|
|
||||||
mGlobalMap->setVisible(mGlobal);
|
mGlobalMap->setVisible(mGlobal);
|
||||||
mLocalMap->setVisible(!mGlobal);
|
mLocalMap->setVisible(!mGlobal);
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace MWGui
|
||||||
|
|
||||||
MyGUI::Widget* getDefaultKeyFocus() override;
|
MyGUI::Widget* getDefaultKeyFocus() override;
|
||||||
|
|
||||||
virtual bool exit() { return false; }
|
virtual bool exit() override { return false; }
|
||||||
|
|
||||||
bool mMarkedToDelete;
|
bool mMarkedToDelete;
|
||||||
|
|
||||||
|
|
|
@ -33,44 +33,41 @@ namespace MWGui
|
||||||
|
|
||||||
QuickKeysMenu::QuickKeysMenu()
|
QuickKeysMenu::QuickKeysMenu()
|
||||||
: WindowBase("openmw_quickkeys_menu.layout")
|
: WindowBase("openmw_quickkeys_menu.layout")
|
||||||
|
, mKey(std::vector<keyData>(10))
|
||||||
|
, mSelected(nullptr)
|
||||||
|
, mActivated(nullptr)
|
||||||
, mAssignDialog(0)
|
, mAssignDialog(0)
|
||||||
, mItemSelectionDialog(0)
|
, mItemSelectionDialog(0)
|
||||||
, mMagicSelectionDialog(0)
|
, mMagicSelectionDialog(0)
|
||||||
, mSelectedIndex(-1)
|
|
||||||
, mActivatedIndex(-1)
|
|
||||||
{
|
{
|
||||||
getWidget(mOkButton, "OKButton");
|
getWidget(mOkButton, "OKButton");
|
||||||
getWidget(mInstructionLabel, "InstructionLabel");
|
getWidget(mInstructionLabel, "InstructionLabel");
|
||||||
|
|
||||||
mMainWidget->setSize(mMainWidget->getWidth(),
|
mMainWidget->setSize(mMainWidget->getWidth(),
|
||||||
mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
|
mMainWidget->getHeight() +
|
||||||
|
(mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
|
||||||
|
|
||||||
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked);
|
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked);
|
||||||
center();
|
center();
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
ItemWidget* button;
|
mKey[i].index = i+1;
|
||||||
getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1));
|
getWidget(mKey[i].button, "QuickKey" + MyGUI::utility::toString(i+1));
|
||||||
|
mKey[i].button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
|
||||||
|
|
||||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
|
unassign(&mKey[i]);
|
||||||
|
|
||||||
mQuickKeyButtons.push_back(button);
|
|
||||||
|
|
||||||
mAssigned.push_back(Type_Unassigned);
|
|
||||||
|
|
||||||
unassign(button, i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::clear()
|
void QuickKeysMenu::clear()
|
||||||
{
|
{
|
||||||
mActivatedIndex = -1;
|
mActivated = nullptr;
|
||||||
|
|
||||||
for (int i=0; i<10; ++i)
|
for (int i=0; i<10; ++i)
|
||||||
{
|
{
|
||||||
unassign(mQuickKeyButtons[i], i);
|
unassign(&mKey[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,10 +88,7 @@ namespace MWGui
|
||||||
// Check if quick keys are still valid
|
// Check if quick keys are still valid
|
||||||
for (int i=0; i<10; ++i)
|
for (int i=0; i<10; ++i)
|
||||||
{
|
{
|
||||||
ItemWidget* button = mQuickKeyButtons[i];
|
switch (mKey[i].type)
|
||||||
int type = mAssigned[i];
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
{
|
||||||
case Type_Unassigned:
|
case Type_Unassigned:
|
||||||
case Type_HandToHand:
|
case Type_HandToHand:
|
||||||
|
@ -103,49 +97,55 @@ namespace MWGui
|
||||||
case Type_Item:
|
case Type_Item:
|
||||||
case Type_MagicItem:
|
case Type_MagicItem:
|
||||||
{
|
{
|
||||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
MWWorld::Ptr item = *mKey[i].button->getUserData<MWWorld::Ptr>();
|
||||||
// Make sure the item is available and is not broken
|
// Make sure the item is available and is not broken
|
||||||
if (item.getRefData().getCount() < 1 ||
|
if (!item || item.getRefData().getCount() < 1 ||
|
||||||
(item.getClass().hasItemHealth(item) &&
|
(item.getClass().hasItemHealth(item) &&
|
||||||
item.getClass().getItemHealth(item) <= 0))
|
item.getClass().getItemHealth(item) <= 0))
|
||||||
{
|
{
|
||||||
// Try searching for a compatible replacement
|
// Try searching for a compatible replacement
|
||||||
std::string id = item.getCellRef().getRefId();
|
item = store.findReplacement(mKey[i].id);
|
||||||
|
|
||||||
|
if (item)
|
||||||
|
mKey[i].button->setUserData(MWWorld::Ptr(item));
|
||||||
|
|
||||||
item = store.findReplacement(id);
|
|
||||||
button->setUserData(MWWorld::Ptr(item));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::unassign(ItemWidget* key, int index)
|
void QuickKeysMenu::unassign(keyData* key)
|
||||||
{
|
{
|
||||||
key->clearUserStrings();
|
key->button->clearUserStrings();
|
||||||
key->setItem(MWWorld::Ptr());
|
key->button->setItem(MWWorld::Ptr());
|
||||||
while (key->getChildCount()) // Destroy number label
|
|
||||||
MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0));
|
|
||||||
|
|
||||||
if (index == 9)
|
while (key->button->getChildCount()) // Destroy number label
|
||||||
|
MyGUI::Gui::getInstance().destroyWidget(key->button->getChildAt(0));
|
||||||
|
|
||||||
|
if (key->index == 10)
|
||||||
{
|
{
|
||||||
mAssigned[index] = Type_HandToHand;
|
key->type = Type_HandToHand;
|
||||||
|
|
||||||
MyGUI::ImageBox* image = key->createWidget<MyGUI::ImageBox>("ImageBox",
|
MyGUI::ImageBox* image = key->button->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||||
MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default);
|
MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default);
|
||||||
|
|
||||||
image->setImageTexture("icons\\k\\stealth_handtohand.dds");
|
image->setImageTexture("icons\\k\\stealth_handtohand.dds");
|
||||||
image->setNeedMouseFocus(false);
|
image->setNeedMouseFocus(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mAssigned[index] = Type_Unassigned;
|
key->type = Type_Unassigned;
|
||||||
|
key->id = "";
|
||||||
|
key->name = "";
|
||||||
|
|
||||||
MyGUI::TextBox* textBox = key->createWidgetReal<MyGUI::TextBox>("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
|
MyGUI::TextBox* textBox = key->button->createWidgetReal<MyGUI::TextBox>("SandText",
|
||||||
textBox->setTextAlign (MyGUI::Align::Center);
|
MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
|
||||||
textBox->setCaption (MyGUI::utility::toString(index+1));
|
|
||||||
textBox->setNeedMouseFocus (false);
|
textBox->setTextAlign(MyGUI::Align::Center);
|
||||||
|
textBox->setCaption(MyGUI::utility::toString(key->index));
|
||||||
|
textBox->setNeedMouseFocus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,19 +154,24 @@ namespace MWGui
|
||||||
int index = -1;
|
int index = -1;
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i])
|
if (sender == mKey[i].button || sender->getParent() == mKey[i].button)
|
||||||
{
|
{
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(index != -1);
|
assert(index != -1);
|
||||||
mSelectedIndex = index;
|
mSelected = &mKey[index];
|
||||||
|
|
||||||
|
// prevent reallocation of zero key from Type_HandToHand
|
||||||
|
if(mSelected->index == 10)
|
||||||
|
return;
|
||||||
|
|
||||||
// open assign dialog
|
// open assign dialog
|
||||||
if (!mAssignDialog)
|
if (!mAssignDialog)
|
||||||
mAssignDialog = new QuickKeysMenuAssign(this);
|
mAssignDialog = new QuickKeysMenuAssign(this);
|
||||||
mAssignDialog->setVisible (true);
|
|
||||||
|
mAssignDialog->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)
|
void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)
|
||||||
|
@ -174,10 +179,9 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)
|
void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)
|
||||||
{
|
{
|
||||||
if (!mItemSelectionDialog )
|
if (!mItemSelectionDialog)
|
||||||
{
|
{
|
||||||
mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}");
|
mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}");
|
||||||
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);
|
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);
|
||||||
|
@ -187,43 +191,45 @@ namespace MWGui
|
||||||
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
|
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
|
||||||
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
|
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
|
||||||
|
|
||||||
mAssignDialog->setVisible (false);
|
mAssignDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)
|
void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)
|
||||||
{
|
{
|
||||||
if (!mMagicSelectionDialog )
|
if (!mMagicSelectionDialog)
|
||||||
{
|
{
|
||||||
mMagicSelectionDialog = new MagicSelectionDialog(this);
|
mMagicSelectionDialog = new MagicSelectionDialog(this);
|
||||||
}
|
}
|
||||||
mMagicSelectionDialog->setVisible(true);
|
mMagicSelectionDialog->setVisible(true);
|
||||||
|
|
||||||
mAssignDialog->setVisible (false);
|
mAssignDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)
|
void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)
|
||||||
{
|
{
|
||||||
unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex);
|
unassign(mSelected);
|
||||||
mAssignDialog->setVisible (false);
|
mAssignDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)
|
void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)
|
||||||
{
|
{
|
||||||
mAssignDialog->setVisible (false);
|
mAssignDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
|
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
|
||||||
{
|
{
|
||||||
assert (mSelectedIndex >= 0);
|
assert(mSelected);
|
||||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
|
||||||
while (button->getChildCount()) // Destroy number label
|
|
||||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
|
||||||
|
|
||||||
mAssigned[mSelectedIndex] = Type_Item;
|
while (mSelected->button->getChildCount()) // Destroy number label
|
||||||
|
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
|
||||||
|
|
||||||
button->setItem(item, ItemWidget::Barter);
|
mSelected->type = Type_Item;
|
||||||
button->setUserString ("ToolTipType", "ItemPtr");
|
mSelected->id = item.getCellRef().getRefId();
|
||||||
button->setUserData(MWWorld::Ptr(item));
|
mSelected->name = item.getClass().getName(item);
|
||||||
|
|
||||||
|
mSelected->button->setItem(item, ItemWidget::Barter);
|
||||||
|
mSelected->button->setUserString("ToolTipType", "ItemPtr");
|
||||||
|
mSelected->button->setUserData(item);
|
||||||
|
|
||||||
if (mItemSelectionDialog)
|
if (mItemSelectionDialog)
|
||||||
mItemSelectionDialog->setVisible(false);
|
mItemSelectionDialog->setVisible(false);
|
||||||
|
@ -234,40 +240,39 @@ namespace MWGui
|
||||||
mItemSelectionDialog->setVisible(false);
|
mItemSelectionDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
|
void QuickKeysMenu::onAssignMagicItem(MWWorld::Ptr item)
|
||||||
{
|
{
|
||||||
assert (mSelectedIndex >= 0);
|
assert(mSelected);
|
||||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
|
||||||
while (button->getChildCount()) // Destroy number label
|
|
||||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
|
||||||
|
|
||||||
mAssigned[mSelectedIndex] = Type_MagicItem;
|
while (mSelected->button->getChildCount()) // Destroy number label
|
||||||
|
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
|
||||||
|
|
||||||
button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
mSelected->type = Type_MagicItem;
|
||||||
button->setIcon(item);
|
|
||||||
|
|
||||||
button->setUserString ("ToolTipType", "ItemPtr");
|
mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||||
button->setUserData(MWWorld::Ptr(item));
|
mSelected->button->setIcon(item);
|
||||||
|
|
||||||
|
mSelected->button->setUserString("ToolTipType", "ItemPtr");
|
||||||
|
mSelected->button->setUserData(MWWorld::Ptr(item));
|
||||||
|
|
||||||
if (mMagicSelectionDialog)
|
if (mMagicSelectionDialog)
|
||||||
mMagicSelectionDialog->setVisible(false);
|
mMagicSelectionDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
|
void QuickKeysMenu::onAssignMagic(const std::string& spellId)
|
||||||
{
|
{
|
||||||
assert (mSelectedIndex >= 0);
|
assert(mSelected);
|
||||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
while (mSelected->button->getChildCount()) // Destroy number label
|
||||||
while (button->getChildCount()) // Destroy number label
|
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
|
||||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
|
||||||
|
|
||||||
mAssigned[mSelectedIndex] = Type_Magic;
|
mSelected->type = Type_Magic;
|
||||||
|
mSelected->id = spellId;
|
||||||
|
|
||||||
button->setItem(MWWorld::Ptr());
|
mSelected->button->setItem(MWWorld::Ptr());
|
||||||
button->setUserString ("ToolTipType", "Spell");
|
mSelected->button->setUserString("ToolTipType", "Spell");
|
||||||
button->setUserString ("Spell", spellId);
|
mSelected->button->setUserString("Spell", spellId);
|
||||||
|
|
||||||
const MWWorld::ESMStore &esmStore =
|
const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
|
|
||||||
// use the icon of the first effect
|
// use the icon of the first effect
|
||||||
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);
|
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);
|
||||||
|
@ -280,14 +285,14 @@ namespace MWGui
|
||||||
path.insert(slashPos+1, "b_");
|
path.insert(slashPos+1, "b_");
|
||||||
path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);
|
path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);
|
||||||
|
|
||||||
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||||
button->setIcon(path);
|
mSelected->button->setIcon(path);
|
||||||
|
|
||||||
if (mMagicSelectionDialog)
|
if (mMagicSelectionDialog)
|
||||||
mMagicSelectionDialog->setVisible(false);
|
mMagicSelectionDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::onAssignMagicCancel ()
|
void QuickKeysMenu::onAssignMagicCancel()
|
||||||
{
|
{
|
||||||
mMagicSelectionDialog->setVisible(false);
|
mMagicSelectionDialog->setVisible(false);
|
||||||
}
|
}
|
||||||
|
@ -295,18 +300,17 @@ namespace MWGui
|
||||||
void QuickKeysMenu::updateActivatedQuickKey()
|
void QuickKeysMenu::updateActivatedQuickKey()
|
||||||
{
|
{
|
||||||
// there is no delayed action, nothing to do.
|
// there is no delayed action, nothing to do.
|
||||||
if (mActivatedIndex < 0)
|
if (!mActivated)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
activateQuickKey(mActivatedIndex);
|
activateQuickKey(mActivated->index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickKeysMenu::activateQuickKey(int index)
|
void QuickKeysMenu::activateQuickKey(int index)
|
||||||
{
|
{
|
||||||
assert (index-1 >= 0);
|
assert(index >= 1 && index <= 10);
|
||||||
ItemWidget* button = mQuickKeyButtons[index-1];
|
|
||||||
|
|
||||||
QuickKeyType type = mAssigned[index-1];
|
keyData *key = &mKey[index-1];
|
||||||
|
|
||||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||||
|
@ -319,76 +323,62 @@ namespace MWGui
|
||||||
|| playerStats.getHitRecovery();
|
|| playerStats.getHitRecovery();
|
||||||
|
|
||||||
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
||||||
if (isReturnNeeded && type != Type_Item)
|
|
||||||
|
if (isReturnNeeded && key->type != Type_Item)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isDelayNeeded && key->type != Type_Item)
|
||||||
if (isDelayNeeded && type != Type_Item)
|
|
||||||
{
|
{
|
||||||
mActivatedIndex = index;
|
mActivated = key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mActivatedIndex = -1;
|
|
||||||
|
|
||||||
if (type == Type_Item || type == Type_MagicItem)
|
|
||||||
{
|
{
|
||||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
mActivated = nullptr;
|
||||||
// Make sure the item is available and is not broken
|
}
|
||||||
if (item.getRefData().getCount() < 1 ||
|
|
||||||
(item.getClass().hasItemHealth(item) &&
|
|
||||||
item.getClass().getItemHealth(item) <= 0))
|
|
||||||
{
|
|
||||||
// Try searching for a compatible replacement
|
|
||||||
std::string id = item.getCellRef().getRefId();
|
|
||||||
|
|
||||||
item = store.findReplacement(id);
|
if (key->type == Type_Item || key->type == Type_MagicItem)
|
||||||
button->setUserData(MWWorld::Ptr(item));
|
|
||||||
|
|
||||||
if (item.getRefData().getCount() < 1)
|
|
||||||
{
|
{
|
||||||
// No replacement was found
|
MWWorld::Ptr item = *key->button->getUserData<MWWorld::Ptr>();
|
||||||
MWBase::Environment::get().getWindowManager ()->messageBox (
|
|
||||||
"#{sQuickMenu5} " + item.getClass().getName(item));
|
MWWorld::ContainerStoreIterator it = store.begin();
|
||||||
|
for (; it != store.end(); ++it)
|
||||||
|
{
|
||||||
|
if (*it == item)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it == store.end())
|
||||||
|
item = nullptr;
|
||||||
|
|
||||||
|
// check the item is available and not broken
|
||||||
|
if (!item || item.getRefData().getCount() < 1 ||
|
||||||
|
(item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))
|
||||||
|
{
|
||||||
|
item = store.findReplacement(key->id);
|
||||||
|
|
||||||
|
if (!item || item.getRefData().getCount() < 1)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox(
|
||||||
|
"#{sQuickMenu5} " + key->name);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (type == Type_Magic)
|
if (key->type == Type_Item)
|
||||||
{
|
{
|
||||||
std::string spellId = button->getUserString("Spell");
|
|
||||||
|
|
||||||
// Make sure the player still has this spell
|
|
||||||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
|
||||||
MWMechanics::Spells& spells = stats.getSpells();
|
|
||||||
if (!spells.hasSpell(spellId))
|
|
||||||
{
|
|
||||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox (
|
|
||||||
"#{sQuickMenu5} " + spell->mName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
store.setSelectedEnchantItem(store.end());
|
|
||||||
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
|
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
|
|
||||||
}
|
|
||||||
else if (type == Type_Item)
|
|
||||||
{
|
|
||||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
|
||||||
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
|
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
|
||||||
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() || item.getTypeName() == typeid(ESM::Lockpick).name();
|
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() ||
|
||||||
|
item.getTypeName() == typeid(ESM::Lockpick).name();
|
||||||
|
|
||||||
// delay weapon switching if player is busy
|
// delay weapon switching if player is busy
|
||||||
if (isDelayNeeded && (isWeapon || isTool))
|
if (isDelayNeeded && (isWeapon || isTool))
|
||||||
{
|
{
|
||||||
mActivatedIndex = index;
|
mActivated = key;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isReturnNeeded && (isWeapon || isTool))
|
||||||
// disable weapon switching if player is dead or paralyzed
|
|
||||||
if (isReturnNeeded && (isWeapon || isTool))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -401,21 +391,8 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == Type_MagicItem)
|
else if (key->type == Type_MagicItem)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
|
||||||
|
|
||||||
// retrieve ContainerStoreIterator to the item
|
|
||||||
MWWorld::ContainerStoreIterator it = store.begin();
|
|
||||||
for (; it != store.end(); ++it)
|
|
||||||
{
|
|
||||||
if (*it == item)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(it != store.end());
|
|
||||||
|
|
||||||
// equip, if it can be equipped
|
// equip, if it can be equipped
|
||||||
if (!item.getClass().getEquipmentSlots(item).first.empty())
|
if (!item.getClass().getEquipmentSlots(item).first.empty())
|
||||||
{
|
{
|
||||||
|
@ -429,7 +406,29 @@ namespace MWGui
|
||||||
store.setSelectedEnchantItem(it);
|
store.setSelectedEnchantItem(it);
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
|
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
|
||||||
}
|
}
|
||||||
else if (type == Type_HandToHand)
|
}
|
||||||
|
else if (key->type == Type_Magic)
|
||||||
|
{
|
||||||
|
std::string spellId = key->id;
|
||||||
|
|
||||||
|
// Make sure the player still has this spell
|
||||||
|
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||||
|
MWMechanics::Spells& spells = stats.getSpells();
|
||||||
|
|
||||||
|
if (!spells.hasSpell(spellId))
|
||||||
|
{
|
||||||
|
const ESM::Spell* spell =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.setSelectedEnchantItem(store.end());
|
||||||
|
MWBase::Environment::get().getWindowManager()
|
||||||
|
->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
|
||||||
|
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
|
||||||
|
}
|
||||||
|
else if (key->type == Type_HandToHand)
|
||||||
{
|
{
|
||||||
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
|
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
||||||
|
@ -491,9 +490,9 @@ namespace MWGui
|
||||||
|
|
||||||
for (int i=0; i<10; ++i)
|
for (int i=0; i<10; ++i)
|
||||||
{
|
{
|
||||||
ItemWidget* button = mQuickKeyButtons[i];
|
ItemWidget* button = mKey[i].button;
|
||||||
|
|
||||||
int type = mAssigned[i];
|
int type = mKey[i].type;
|
||||||
|
|
||||||
ESM::QuickKeys::QuickKey key;
|
ESM::QuickKeys::QuickKey key;
|
||||||
key.mType = type;
|
key.mType = type;
|
||||||
|
@ -541,30 +540,29 @@ namespace MWGui
|
||||||
if (i >= 10)
|
if (i >= 10)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mSelectedIndex = i;
|
mSelected = &mKey[i];
|
||||||
int keyType = it->mType;
|
mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType;
|
||||||
std::string id = it->mId;
|
mSelected->id = it->mId;
|
||||||
ItemWidget* button = mQuickKeyButtons[i];
|
|
||||||
|
|
||||||
switch (keyType)
|
switch (mSelected->type)
|
||||||
{
|
{
|
||||||
case Type_Magic:
|
case Type_Magic:
|
||||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id))
|
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(mSelected->id))
|
||||||
onAssignMagic(id);
|
onAssignMagic(mSelected->id);
|
||||||
break;
|
break;
|
||||||
case Type_Item:
|
case Type_Item:
|
||||||
case Type_MagicItem:
|
case Type_MagicItem:
|
||||||
{
|
{
|
||||||
// Find the item by id
|
// Find the item by id
|
||||||
MWWorld::Ptr item = store.findReplacement(id);
|
MWWorld::Ptr item = store.findReplacement(mSelected->id);
|
||||||
|
|
||||||
if (item.isEmpty())
|
if (item.isEmpty())
|
||||||
unassign(button, i);
|
unassign(&mKey[i]);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (keyType == Type_Item)
|
if (mSelected->type == Type_Item)
|
||||||
onAssignItem(item);
|
onAssignItem(item);
|
||||||
else if (keyType == Type_MagicItem)
|
else if (mSelected->type == Type_MagicItem)
|
||||||
onAssignMagicItem(item);
|
onAssignMagicItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,7 +570,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
case Type_Unassigned:
|
case Type_Unassigned:
|
||||||
case Type_HandToHand:
|
case Type_HandToHand:
|
||||||
unassign(button, i);
|
unassign(&mKey[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,23 +55,31 @@ namespace MWGui
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
struct keyData {
|
||||||
|
int index;
|
||||||
|
ItemWidget* button;
|
||||||
|
QuickKeysMenu::QuickKeyType type;
|
||||||
|
std::string id;
|
||||||
|
std::string name;
|
||||||
|
keyData(): index(-1), button(nullptr), type(Type_Unassigned), id(""), name("") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<keyData> mKey;
|
||||||
|
keyData* mSelected;
|
||||||
|
keyData* mActivated;
|
||||||
|
|
||||||
MyGUI::EditBox* mInstructionLabel;
|
MyGUI::EditBox* mInstructionLabel;
|
||||||
MyGUI::Button* mOkButton;
|
MyGUI::Button* mOkButton;
|
||||||
|
|
||||||
std::vector<ItemWidget*> mQuickKeyButtons;
|
|
||||||
std::vector<QuickKeyType> mAssigned;
|
|
||||||
|
|
||||||
QuickKeysMenuAssign* mAssignDialog;
|
QuickKeysMenuAssign* mAssignDialog;
|
||||||
ItemSelectionDialog* mItemSelectionDialog;
|
ItemSelectionDialog* mItemSelectionDialog;
|
||||||
MagicSelectionDialog* mMagicSelectionDialog;
|
MagicSelectionDialog* mMagicSelectionDialog;
|
||||||
|
|
||||||
int mSelectedIndex;
|
|
||||||
int mActivatedIndex;
|
|
||||||
|
|
||||||
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
|
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
|
||||||
void onOkButtonClicked(MyGUI::Widget* sender);
|
void onOkButtonClicked(MyGUI::Widget* sender);
|
||||||
|
|
||||||
void unassign(ItemWidget* key, int index);
|
void unassign(keyData* key);
|
||||||
};
|
};
|
||||||
|
|
||||||
class QuickKeysMenuAssign : public WindowModal
|
class QuickKeysMenuAssign : public WindowModal
|
||||||
|
|
|
@ -80,6 +80,7 @@ namespace MWGui
|
||||||
|
|
||||||
newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId);
|
newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId);
|
||||||
newSpell.mActive = true;
|
newSpell.mActive = true;
|
||||||
|
newSpell.mCount = 1;
|
||||||
mSpells.push_back(newSpell);
|
mSpells.push_back(newSpell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +110,7 @@ namespace MWGui
|
||||||
newSpell.mItem = item;
|
newSpell.mItem = item;
|
||||||
newSpell.mId = item.getCellRef().getRefId();
|
newSpell.mId = item.getCellRef().getRefId();
|
||||||
newSpell.mName = item.getClass().getName(item);
|
newSpell.mName = item.getClass().getName(item);
|
||||||
|
newSpell.mCount = item.getRefData().getCount();
|
||||||
newSpell.mType = Spell::Type_EnchantedItem;
|
newSpell.mType = Spell::Type_EnchantedItem;
|
||||||
newSpell.mSelected = invStore.getSelectedEnchantItem() == it;
|
newSpell.mSelected = invStore.getSelectedEnchantItem() == it;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace MWGui
|
||||||
std::string mCostColumn; // Cost/chance or Cost/charge
|
std::string mCostColumn; // Cost/chance or Cost/charge
|
||||||
std::string mId; // Item ID or spell ID
|
std::string mId; // Item ID or spell ID
|
||||||
MWWorld::Ptr mItem; // Only for Type_EnchantedItem
|
MWWorld::Ptr mItem; // Only for Type_EnchantedItem
|
||||||
|
int mCount; // Only for Type_EnchantedItem
|
||||||
bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time)
|
bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time)
|
||||||
bool mActive; // (Items only) is the item equipped?
|
bool mActive; // (Items only) is the item equipped?
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <components/widgets/sharedstatebutton.hpp>
|
#include <components/widgets/sharedstatebutton.hpp>
|
||||||
|
|
||||||
|
#include "tooltips.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -103,11 +105,12 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped";
|
const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped";
|
||||||
|
const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount);
|
||||||
|
|
||||||
Gui::SharedStateButton* t = mScrollView->createWidget<Gui::SharedStateButton>(skin,
|
Gui::SharedStateButton* t = mScrollView->createWidget<Gui::SharedStateButton>(skin,
|
||||||
MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||||
t->setNeedKeyFocus(true);
|
t->setNeedKeyFocus(true);
|
||||||
t->setCaption(spell.mName);
|
t->setCaption(spell.mName + captionSuffix);
|
||||||
t->setTextAlign(MyGUI::Align::Left);
|
t->setTextAlign(MyGUI::Align::Left);
|
||||||
adjustSpellWidget(spell, i, t);
|
adjustSpellWidget(spell, i, t);
|
||||||
|
|
||||||
|
@ -163,7 +166,8 @@ namespace MWGui
|
||||||
|
|
||||||
// more checking for major change.
|
// more checking for major change.
|
||||||
const Spell& spell = mModel->getItem(spellIndex);
|
const Spell& spell = mModel->getItem(spellIndex);
|
||||||
if (nameButton->getCaption() != spell.mName)
|
const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount);
|
||||||
|
if (nameButton->getCaption() != (spell.mName + captionSuffix))
|
||||||
{
|
{
|
||||||
fullUpdateRequired = true;
|
fullUpdateRequired = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -191,6 +191,9 @@ namespace MWGui
|
||||||
else if (type == "ItemPtr")
|
else if (type == "ItemPtr")
|
||||||
{
|
{
|
||||||
mFocusObject = *focus->getUserData<MWWorld::Ptr>();
|
mFocusObject = *focus->getUserData<MWWorld::Ptr>();
|
||||||
|
if (!mFocusObject)
|
||||||
|
return;
|
||||||
|
|
||||||
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
|
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
|
||||||
}
|
}
|
||||||
else if (type == "ItemModelIndex")
|
else if (type == "ItemModelIndex")
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace MWGui
|
||||||
|
|
||||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||||
{
|
{
|
||||||
int value = npcStats.getSkill (i).getBase ();
|
int value = npcStats.getSkill (i).getModified ();
|
||||||
|
|
||||||
skills.push_back(std::make_pair(i, value));
|
skills.push_back(std::make_pair(i, value));
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ namespace MWGui
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr);
|
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr);
|
||||||
if (npcStats.getSkill (skillId).getBase () <= pcStats.getSkill (skillId).getBase ())
|
if (npcStats.getSkill (skillId).getModified () <= pcStats.getSkill (skillId).getBase ())
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}");
|
MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -56,15 +56,15 @@ namespace MWGui
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Modal" windows cause the rest of the interface to be unaccessible while they are visible
|
* "Modal" windows cause the rest of the interface to be inaccessible while they are visible
|
||||||
*/
|
*/
|
||||||
class WindowModal : public WindowBase
|
class WindowModal : public WindowBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WindowModal(const std::string& parLayout);
|
WindowModal(const std::string& parLayout);
|
||||||
virtual void onOpen();
|
virtual void onOpen() override;
|
||||||
virtual void onClose();
|
virtual void onClose() override;
|
||||||
virtual bool exit() {return true;}
|
virtual bool exit() override {return true;}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A window that cannot be the target of a drag&drop action.
|
/// A window that cannot be the target of a drag&drop action.
|
||||||
|
|
|
@ -1328,10 +1328,10 @@ namespace MWGui
|
||||||
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
|
MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; }
|
||||||
MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
|
MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
|
||||||
|
|
||||||
void WindowManager::useItem(const MWWorld::Ptr &item)
|
void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
|
||||||
{
|
{
|
||||||
if (mInventoryWindow)
|
if (mInventoryWindow)
|
||||||
mInventoryWindow->useItem(item);
|
mInventoryWindow->useItem(item, bypassBeastRestrictions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowManager::isAllowed (GuiWindow wnd) const
|
bool WindowManager::isAllowed (GuiWindow wnd) const
|
||||||
|
|
|
@ -185,7 +185,7 @@ namespace MWGui
|
||||||
virtual MWGui::TradeWindow* getTradeWindow();
|
virtual MWGui::TradeWindow* getTradeWindow();
|
||||||
|
|
||||||
/// Make the player use an item, while updating GUI state accordingly
|
/// Make the player use an item, while updating GUI state accordingly
|
||||||
virtual void useItem(const MWWorld::Ptr& item);
|
virtual void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false);
|
||||||
|
|
||||||
virtual void updateSpellWindow();
|
virtual void updateSpellWindow();
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
|
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
|
||||||
{
|
{
|
||||||
mCharacterController.reset(new CharacterController(ptr, animation));
|
mCharacterController.reset(new CharacterController(ptr, animation));
|
||||||
|
@ -19,10 +18,4 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return mCharacterController.get();
|
return mCharacterController.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
AiState& Actor::getAiState()
|
|
||||||
{
|
|
||||||
return mAiState;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "aistate.hpp"
|
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class Animation;
|
class Animation;
|
||||||
|
@ -29,12 +27,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
CharacterController* getCharacterController();
|
CharacterController* getCharacterController();
|
||||||
|
|
||||||
AiState& getAiState();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<CharacterController> mCharacterController;
|
std::unique_ptr<CharacterController> mCharacterController;
|
||||||
|
|
||||||
AiState mAiState;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -766,6 +766,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||||
{
|
{
|
||||||
|
bool actorKilled = false;
|
||||||
|
|
||||||
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
||||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
||||||
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
||||||
|
@ -793,10 +795,14 @@ namespace MWMechanics
|
||||||
caster.getClass().getNpcStats(caster).addWerewolfKill();
|
caster.getClass().getNpcStats(caster).addWerewolfKill();
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
|
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
|
||||||
|
actorKilled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actorKilled)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,6 +1163,13 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell)
|
||||||
|
{
|
||||||
|
PtrActorMap::iterator iter = mActors.find(ptr);
|
||||||
|
if(iter != mActors.end())
|
||||||
|
iter->second->getCharacterController()->castSpell(spellId, manualSpell);
|
||||||
|
}
|
||||||
|
|
||||||
bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
|
bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
|
||||||
{
|
{
|
||||||
if (!actor.getClass().isActor())
|
if (!actor.getClass().isActor())
|
||||||
|
@ -1365,7 +1378,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
|
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||||
if (isConscious(iter->first))
|
if (isConscious(iter->first))
|
||||||
stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), iter->second->getAiState(), duration);
|
stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1968,6 +1981,15 @@ namespace MWMechanics
|
||||||
return it->second->getCharacterController()->isReadyToBlock();
|
return it->second->getCharacterController()->isReadyToBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Actors::isCastingSpell(const MWWorld::Ptr &ptr) const
|
||||||
|
{
|
||||||
|
PtrActorMap::const_iterator it = mActors.find(ptr);
|
||||||
|
if (it == mActors.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return it->second->getCharacterController()->isCastingSpell();
|
||||||
|
}
|
||||||
|
|
||||||
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
|
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
PtrActorMap::const_iterator it = mActors.find(ptr);
|
PtrActorMap::const_iterator it = mActors.find(ptr);
|
||||||
|
@ -1993,7 +2015,7 @@ namespace MWMechanics
|
||||||
|| ptr.getClass().getCreatureStats(ptr).isParalyzed())
|
|| ptr.getClass().getCreatureStats(ptr).isParalyzed())
|
||||||
continue;
|
continue;
|
||||||
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||||
seq.fastForward(ptr, it->second->getAiState());
|
seq.fastForward(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,8 @@ namespace MWMechanics
|
||||||
///
|
///
|
||||||
/// \note Ignored, if \a ptr is not a registered actor.
|
/// \note Ignored, if \a ptr is not a registered actor.
|
||||||
|
|
||||||
|
void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
|
||||||
|
|
||||||
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
|
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
|
||||||
///< Updates an actor with a new Ptr
|
///< Updates an actor with a new Ptr
|
||||||
|
|
||||||
|
@ -161,6 +163,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
void clear(); // Clear death counter
|
void clear(); // Clear death counter
|
||||||
|
|
||||||
|
bool isCastingSpell(const MWWorld::Ptr& ptr) const;
|
||||||
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
||||||
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
|
|
84
apps/openmw/mwmechanics/aicast.cpp
Normal file
84
apps/openmw/mwmechanics/aicast.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include "aicast.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "aicombataction.hpp"
|
||||||
|
#include "creaturestats.hpp"
|
||||||
|
#include "spellcasting.hpp"
|
||||||
|
#include "steering.hpp"
|
||||||
|
|
||||||
|
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
|
||||||
|
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
|
||||||
|
{
|
||||||
|
ActionSpell action = ActionSpell(spellId);
|
||||||
|
bool isRanged;
|
||||||
|
mDistance = action.getCombatRange(isRanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWMechanics::AiPackage *MWMechanics::AiCast::clone() const
|
||||||
|
{
|
||||||
|
return new AiCast(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr target;
|
||||||
|
if (actor.getCellRef().getRefId() == mTargetId)
|
||||||
|
{
|
||||||
|
// If the target has the same ID as caster, consider that actor casts spell with Self range.
|
||||||
|
target = actor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = getTarget();
|
||||||
|
if (!target)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!mManual && !pathTo(actor, target.getRefData().getPosition().pos, duration, mDistance))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec3f dir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();
|
||||||
|
bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
|
||||||
|
turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));
|
||||||
|
|
||||||
|
if (!turned)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the actor is already casting another spell
|
||||||
|
bool isCasting = MWBase::Environment::get().getMechanicsManager()->isCastingSpell(actor);
|
||||||
|
if (isCasting && !mCasting)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!mCasting)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->castSpell(actor, mSpellId, mManual);
|
||||||
|
mCasting = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish package, if actor finished spellcasting
|
||||||
|
return !isCasting;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr MWMechanics::AiCast::getTarget() const
|
||||||
|
{
|
||||||
|
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mTargetId, false);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MWMechanics::AiCast::getTypeId() const
|
||||||
|
{
|
||||||
|
return AiPackage::TypeIdCast;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int MWMechanics::AiCast::getPriority() const
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
37
apps/openmw/mwmechanics/aicast.hpp
Normal file
37
apps/openmw/mwmechanics/aicast.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef GAME_MWMECHANICS_AICAST_H
|
||||||
|
#define GAME_MWMECHANICS_AICAST_H
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "aipackage.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
/// AiPackage which makes an actor to cast given spell.
|
||||||
|
class AiCast : public AiPackage {
|
||||||
|
public:
|
||||||
|
AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false);
|
||||||
|
|
||||||
|
virtual AiPackage *clone() const;
|
||||||
|
|
||||||
|
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
|
||||||
|
|
||||||
|
virtual int getTypeId() const;
|
||||||
|
|
||||||
|
virtual MWWorld::Ptr getTarget() const;
|
||||||
|
|
||||||
|
virtual unsigned int getPriority() const;
|
||||||
|
|
||||||
|
virtual bool canCancel() const { return false; }
|
||||||
|
virtual bool shouldCancelPreviousAi() const { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mTargetId;
|
||||||
|
std::string mSpellId;
|
||||||
|
bool mCasting;
|
||||||
|
bool mManual;
|
||||||
|
float mDistance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -35,74 +35,6 @@ namespace
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
/// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
|
|
||||||
struct AiCombatStorage : AiTemporaryBase
|
|
||||||
{
|
|
||||||
float mAttackCooldown;
|
|
||||||
float mTimerReact;
|
|
||||||
float mTimerCombatMove;
|
|
||||||
bool mReadyToAttack;
|
|
||||||
bool mAttack;
|
|
||||||
float mAttackRange;
|
|
||||||
bool mCombatMove;
|
|
||||||
osg::Vec3f mLastTargetPos;
|
|
||||||
const MWWorld::CellStore* mCell;
|
|
||||||
std::shared_ptr<Action> mCurrentAction;
|
|
||||||
float mActionCooldown;
|
|
||||||
float mStrength;
|
|
||||||
bool mForceNoShortcut;
|
|
||||||
ESM::Position mShortcutFailPos;
|
|
||||||
MWMechanics::Movement mMovement;
|
|
||||||
|
|
||||||
enum FleeState
|
|
||||||
{
|
|
||||||
FleeState_None,
|
|
||||||
FleeState_Idle,
|
|
||||||
FleeState_RunBlindly,
|
|
||||||
FleeState_RunToDestination
|
|
||||||
};
|
|
||||||
FleeState mFleeState;
|
|
||||||
bool mLOS;
|
|
||||||
float mUpdateLOSTimer;
|
|
||||||
float mFleeBlindRunTimer;
|
|
||||||
ESM::Pathgrid::Point mFleeDest;
|
|
||||||
|
|
||||||
AiCombatStorage():
|
|
||||||
mAttackCooldown(0.0f),
|
|
||||||
mTimerReact(AI_REACTION_TIME),
|
|
||||||
mTimerCombatMove(0.0f),
|
|
||||||
mReadyToAttack(false),
|
|
||||||
mAttack(false),
|
|
||||||
mAttackRange(0.0f),
|
|
||||||
mCombatMove(false),
|
|
||||||
mLastTargetPos(0,0,0),
|
|
||||||
mCell(NULL),
|
|
||||||
mCurrentAction(),
|
|
||||||
mActionCooldown(0.0f),
|
|
||||||
mStrength(),
|
|
||||||
mForceNoShortcut(false),
|
|
||||||
mShortcutFailPos(),
|
|
||||||
mMovement(),
|
|
||||||
mFleeState(FleeState_None),
|
|
||||||
mLOS(false),
|
|
||||||
mUpdateLOSTimer(0.0f),
|
|
||||||
mFleeBlindRunTimer(0.0f)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
|
||||||
void updateCombatMove(float duration);
|
|
||||||
void stopCombatMove();
|
|
||||||
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
|
|
||||||
const ESM::Weapon* weapon, bool distantCombat);
|
|
||||||
void updateAttack(CharacterController& characterController);
|
|
||||||
void stopAttack();
|
|
||||||
|
|
||||||
void startFleeing();
|
|
||||||
void stopFleeing();
|
|
||||||
bool isFleeing();
|
|
||||||
};
|
|
||||||
|
|
||||||
AiCombat::AiCombat(const MWWorld::Ptr& actor)
|
AiCombat::AiCombat(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||||
|
|
|
@ -23,7 +23,72 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
class Action;
|
class Action;
|
||||||
|
|
||||||
struct AiCombatStorage;
|
/// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
|
||||||
|
struct AiCombatStorage : AiTemporaryBase
|
||||||
|
{
|
||||||
|
float mAttackCooldown;
|
||||||
|
float mTimerReact;
|
||||||
|
float mTimerCombatMove;
|
||||||
|
bool mReadyToAttack;
|
||||||
|
bool mAttack;
|
||||||
|
float mAttackRange;
|
||||||
|
bool mCombatMove;
|
||||||
|
osg::Vec3f mLastTargetPos;
|
||||||
|
const MWWorld::CellStore* mCell;
|
||||||
|
std::shared_ptr<Action> mCurrentAction;
|
||||||
|
float mActionCooldown;
|
||||||
|
float mStrength;
|
||||||
|
bool mForceNoShortcut;
|
||||||
|
ESM::Position mShortcutFailPos;
|
||||||
|
MWMechanics::Movement mMovement;
|
||||||
|
|
||||||
|
enum FleeState
|
||||||
|
{
|
||||||
|
FleeState_None,
|
||||||
|
FleeState_Idle,
|
||||||
|
FleeState_RunBlindly,
|
||||||
|
FleeState_RunToDestination
|
||||||
|
};
|
||||||
|
FleeState mFleeState;
|
||||||
|
bool mLOS;
|
||||||
|
float mUpdateLOSTimer;
|
||||||
|
float mFleeBlindRunTimer;
|
||||||
|
ESM::Pathgrid::Point mFleeDest;
|
||||||
|
|
||||||
|
AiCombatStorage():
|
||||||
|
mAttackCooldown(0.0f),
|
||||||
|
mTimerReact(AI_REACTION_TIME),
|
||||||
|
mTimerCombatMove(0.0f),
|
||||||
|
mReadyToAttack(false),
|
||||||
|
mAttack(false),
|
||||||
|
mAttackRange(0.0f),
|
||||||
|
mCombatMove(false),
|
||||||
|
mLastTargetPos(0,0,0),
|
||||||
|
mCell(NULL),
|
||||||
|
mCurrentAction(),
|
||||||
|
mActionCooldown(0.0f),
|
||||||
|
mStrength(),
|
||||||
|
mForceNoShortcut(false),
|
||||||
|
mShortcutFailPos(),
|
||||||
|
mMovement(),
|
||||||
|
mFleeState(FleeState_None),
|
||||||
|
mLOS(false),
|
||||||
|
mUpdateLOSTimer(0.0f),
|
||||||
|
mFleeBlindRunTimer(0.0f)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
||||||
|
void updateCombatMove(float duration);
|
||||||
|
void stopCombatMove();
|
||||||
|
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
|
||||||
|
const ESM::Weapon* weapon, bool distantCombat);
|
||||||
|
void updateAttack(CharacterController& characterController);
|
||||||
|
void stopAttack();
|
||||||
|
|
||||||
|
void startFleeing();
|
||||||
|
void stopFleeing();
|
||||||
|
bool isFleeing();
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Causes the actor to fight another actor
|
/// \brief Causes the actor to fight another actor
|
||||||
class AiCombat : public AiPackage
|
class AiCombat : public AiPackage
|
||||||
|
|
|
@ -17,22 +17,6 @@
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
struct AiFollowStorage : AiTemporaryBase
|
|
||||||
{
|
|
||||||
float mTimer;
|
|
||||||
bool mMoving;
|
|
||||||
float mTargetAngleRadians;
|
|
||||||
bool mTurnActorToTarget;
|
|
||||||
|
|
||||||
AiFollowStorage() :
|
|
||||||
mTimer(0.f),
|
|
||||||
mMoving(false),
|
|
||||||
mTargetAngleRadians(0.f),
|
|
||||||
mTurnActorToTarget(false)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
int AiFollow::mFollowIndexCounter = 0;
|
int AiFollow::mFollowIndexCounter = 0;
|
||||||
|
|
||||||
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
|
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
|
||||||
|
|
|
@ -19,6 +19,21 @@ namespace AiSequence
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
struct AiFollowStorage : AiTemporaryBase
|
||||||
|
{
|
||||||
|
float mTimer;
|
||||||
|
bool mMoving;
|
||||||
|
float mTargetAngleRadians;
|
||||||
|
bool mTurnActorToTarget;
|
||||||
|
|
||||||
|
AiFollowStorage() :
|
||||||
|
mTimer(0.f),
|
||||||
|
mMoving(false),
|
||||||
|
mTargetAngleRadians(0.f),
|
||||||
|
mTurnActorToTarget(false)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief AiPackage for an actor to follow another actor/the PC
|
/// \brief AiPackage for an actor to follow another actor/the PC
|
||||||
/** 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
|
/** 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
|
||||||
**/
|
**/
|
||||||
|
|
|
@ -49,7 +49,8 @@ namespace MWMechanics
|
||||||
TypeIdAvoidDoor = 7,
|
TypeIdAvoidDoor = 7,
|
||||||
TypeIdFace = 8,
|
TypeIdFace = 8,
|
||||||
TypeIdBreathe = 9,
|
TypeIdBreathe = 9,
|
||||||
TypeIdInternalTravel = 10
|
TypeIdInternalTravel = 10,
|
||||||
|
TypeIdCast = 11
|
||||||
};
|
};
|
||||||
|
|
||||||
///Default constructor
|
///Default constructor
|
||||||
|
|
|
@ -180,15 +180,11 @@ bool AiSequence::isPackageDone() const
|
||||||
|
|
||||||
bool isActualAiPackage(int packageTypeId)
|
bool isActualAiPackage(int packageTypeId)
|
||||||
{
|
{
|
||||||
return (packageTypeId != AiPackage::TypeIdCombat
|
return (packageTypeId >= AiPackage::TypeIdWander &&
|
||||||
&& packageTypeId != AiPackage::TypeIdPursue
|
packageTypeId <= AiPackage::TypeIdActivate);
|
||||||
&& packageTypeId != AiPackage::TypeIdAvoidDoor
|
|
||||||
&& packageTypeId != AiPackage::TypeIdFace
|
|
||||||
&& packageTypeId != AiPackage::TypeIdBreathe
|
|
||||||
&& packageTypeId != AiPackage::TypeIdInternalTravel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration)
|
||||||
{
|
{
|
||||||
if(actor != getPlayer())
|
if(actor != getPlayer())
|
||||||
{
|
{
|
||||||
|
@ -262,7 +258,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (package->execute (actor,characterController,state,duration))
|
if (package->execute (actor, characterController, mAiState, duration))
|
||||||
{
|
{
|
||||||
// Put repeating noncombat AI packages on the end of the stack so they can be used again
|
// Put repeating noncombat AI packages on the end of the stack so they can be used again
|
||||||
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
|
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
|
||||||
|
@ -308,7 +304,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
||||||
if (isActualAiPackage(package.getTypeId()))
|
if (isActualAiPackage(package.getTypeId()))
|
||||||
stopCombat();
|
stopCombat();
|
||||||
|
|
||||||
// We should return a wandering actor back after combat or pursuit.
|
// We should return a wandering actor back after combat, casting or pursuit.
|
||||||
// The same thing for actors without AI packages.
|
// The same thing for actors without AI packages.
|
||||||
// Also there is no point to stack return packages.
|
// Also there is no point to stack return packages.
|
||||||
int currentTypeId = getTypeId();
|
int currentTypeId = getTypeId();
|
||||||
|
@ -316,7 +312,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
||||||
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
||||||
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
||||||
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
||||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue))
|
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue
|
||||||
|
|| newTypeId == MWMechanics::AiPackage::TypeIdCast))
|
||||||
{
|
{
|
||||||
osg::Vec3f dest;
|
osg::Vec3f dest;
|
||||||
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
|
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
|
||||||
|
@ -352,6 +349,13 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
||||||
// insert new package in correct place depending on priority
|
// insert new package in correct place depending on priority
|
||||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||||
{
|
{
|
||||||
|
// We should keep current AiCast package, if we try to add a new one.
|
||||||
|
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast &&
|
||||||
|
package.getTypeId() == MWMechanics::AiPackage::TypeIdCast)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if((*it)->getPriority() <= package.getPriority())
|
if((*it)->getPriority() <= package.getPriority())
|
||||||
{
|
{
|
||||||
mPackages.insert(it,package.clone());
|
mPackages.insert(it,package.clone());
|
||||||
|
@ -360,6 +364,14 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
mPackages.push_back (package.clone());
|
mPackages.push_back (package.clone());
|
||||||
|
|
||||||
|
// Make sure that temporary storage is empty
|
||||||
|
if (cancelOther)
|
||||||
|
{
|
||||||
|
mAiState.moveIn(new AiCombatStorage());
|
||||||
|
mAiState.moveIn(new AiFollowStorage());
|
||||||
|
mAiState.moveIn(new AiWanderStorage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AiPackage* MWMechanics::AiSequence::getActivePackage()
|
AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||||
|
@ -494,12 +506,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
mLastAiPackage = sequence.mLastAiPackage;
|
mLastAiPackage = sequence.mLastAiPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
void AiSequence::fastForward(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
if (!mPackages.empty())
|
if (!mPackages.empty())
|
||||||
{
|
{
|
||||||
MWMechanics::AiPackage* package = mPackages.front();
|
MWMechanics::AiPackage* package = mPackages.front();
|
||||||
package->fastForward(actor, state);
|
package->fastForward(actor, mAiState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include "aistate.hpp"
|
||||||
|
|
||||||
#include <components/esm/loadnpc.hpp>
|
#include <components/esm/loadnpc.hpp>
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
|
@ -47,6 +49,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
/// The type of AI package that ran last
|
/// The type of AI package that ran last
|
||||||
int mLastAiPackage;
|
int mLastAiPackage;
|
||||||
|
AiState mAiState;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
///Default constructor
|
///Default constructor
|
||||||
|
@ -104,10 +107,10 @@ namespace MWMechanics
|
||||||
void stopPursuit();
|
void stopPursuit();
|
||||||
|
|
||||||
/// Execute current package, switching if needed.
|
/// Execute current package, switching if needed.
|
||||||
void execute (const MWWorld::Ptr& actor, CharacterController& characterController, MWMechanics::AiState& state, float duration);
|
void execute (const MWWorld::Ptr& actor, CharacterController& characterController, float duration);
|
||||||
|
|
||||||
/// Simulate the passing of time using the currently active AI package
|
/// Simulate the passing of time using the currently active AI package
|
||||||
void fastForward(const MWWorld::Ptr &actor, AiState &state);
|
void fastForward(const MWWorld::Ptr &actor);
|
||||||
|
|
||||||
/// Remove all packages.
|
/// Remove all packages.
|
||||||
void clear();
|
void clear();
|
||||||
|
|
|
@ -53,7 +53,23 @@ namespace MWMechanics
|
||||||
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (pathTo(actor, ESM::Pathgrid::Point(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ)), duration))
|
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
||||||
|
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
||||||
|
int destinationTolerance = 64;
|
||||||
|
ESM::Pathgrid::Point dest(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ));
|
||||||
|
if (distance(pos.pos, dest) <= destinationTolerance)
|
||||||
|
{
|
||||||
|
std::vector<MWWorld::Ptr> targetActors;
|
||||||
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors);
|
||||||
|
|
||||||
|
if (!result.first.isEmpty())
|
||||||
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathTo(actor, dest, duration))
|
||||||
{
|
{
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -51,67 +51,6 @@ namespace MWMechanics
|
||||||
std::string("idle9"),
|
std::string("idle9"),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
|
||||||
struct AiWanderStorage : AiTemporaryBase
|
|
||||||
{
|
|
||||||
// the z rotation angle to reach
|
|
||||||
// when mTurnActorGivingGreetingToFacePlayer is true
|
|
||||||
float mTargetAngleRadians;
|
|
||||||
bool mTurnActorGivingGreetingToFacePlayer;
|
|
||||||
float mReaction; // update some actions infrequently
|
|
||||||
|
|
||||||
AiWander::GreetingState mSaidGreeting;
|
|
||||||
int mGreetingTimer;
|
|
||||||
|
|
||||||
const MWWorld::CellStore* mCell; // for detecting cell change
|
|
||||||
|
|
||||||
// AiWander states
|
|
||||||
AiWander::WanderState mState;
|
|
||||||
|
|
||||||
bool mIsWanderingManually;
|
|
||||||
bool mCanWanderAlongPathGrid;
|
|
||||||
|
|
||||||
unsigned short mIdleAnimation;
|
|
||||||
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
|
|
||||||
|
|
||||||
// do we need to calculate allowed nodes based on mDistance
|
|
||||||
bool mPopulateAvailableNodes;
|
|
||||||
|
|
||||||
// allowed pathgrid nodes based on mDistance from the spawn point
|
|
||||||
// in local coordinates of mCell
|
|
||||||
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
|
|
||||||
|
|
||||||
ESM::Pathgrid::Point mCurrentNode;
|
|
||||||
bool mTrimCurrentNode;
|
|
||||||
|
|
||||||
float mDoorCheckDuration;
|
|
||||||
int mStuckCount;
|
|
||||||
|
|
||||||
AiWanderStorage():
|
|
||||||
mTargetAngleRadians(0),
|
|
||||||
mTurnActorGivingGreetingToFacePlayer(false),
|
|
||||||
mReaction(0),
|
|
||||||
mSaidGreeting(AiWander::Greet_None),
|
|
||||||
mGreetingTimer(0),
|
|
||||||
mCell(NULL),
|
|
||||||
mState(AiWander::Wander_ChooseAction),
|
|
||||||
mIsWanderingManually(false),
|
|
||||||
mCanWanderAlongPathGrid(true),
|
|
||||||
mIdleAnimation(0),
|
|
||||||
mBadIdles(),
|
|
||||||
mPopulateAvailableNodes(true),
|
|
||||||
mAllowedNodes(),
|
|
||||||
mTrimCurrentNode(false),
|
|
||||||
mDoorCheckDuration(0), // TODO: maybe no longer needed
|
|
||||||
mStuckCount(0)
|
|
||||||
{};
|
|
||||||
|
|
||||||
void setState(const AiWander::WanderState wanderState, const bool isManualWander = false) {
|
|
||||||
mState = wanderState;
|
|
||||||
mIsWanderingManually = isManualWander;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
||||||
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
|
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0))
|
||||||
|
@ -221,7 +160,7 @@ namespace MWMechanics
|
||||||
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
|
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
storage.setState(Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
}
|
}
|
||||||
|
|
||||||
doPerFrameActionsForState(actor, duration, storage, pos);
|
doPerFrameActionsForState(actor, duration, storage, pos);
|
||||||
|
@ -270,7 +209,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if(actorCanMoveByZ && mDistance > 0) {
|
if(actorCanMoveByZ && mDistance > 0) {
|
||||||
// Typically want to idle for a short time before the next wander
|
// Typically want to idle for a short time before the next wander
|
||||||
if (Misc::Rng::rollDice(100) >= 92 && storage.mState != Wander_Walking) {
|
if (Misc::Rng::rollDice(100) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {
|
||||||
wanderNearStart(actor, storage, mDistance);
|
wanderNearStart(actor, storage, mDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +222,7 @@ namespace MWMechanics
|
||||||
if (Misc::Rng::rollDice(100) >= 96) {
|
if (Misc::Rng::rollDice(100) >= 96) {
|
||||||
wanderNearStart(actor, storage, mDistance);
|
wanderNearStart(actor, storage, mDistance);
|
||||||
} else {
|
} else {
|
||||||
storage.setState(Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
}
|
}
|
||||||
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
|
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
|
||||||
storage.mCanWanderAlongPathGrid = false;
|
storage.mCanWanderAlongPathGrid = false;
|
||||||
|
@ -299,13 +238,13 @@ namespace MWMechanics
|
||||||
mDistance = 0;
|
mDistance = 0;
|
||||||
|
|
||||||
// Allow interrupting a walking actor to trigger a greeting
|
// Allow interrupting a walking actor to trigger a greeting
|
||||||
WanderState& wanderState = storage.mState;
|
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||||
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking))
|
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
||||||
{
|
{
|
||||||
playGreetingIfPlayerGetsTooClose(actor, storage);
|
playGreetingIfPlayerGetsTooClose(actor, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((wanderState == Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
||||||
{
|
{
|
||||||
// Construct a new path if there isn't one
|
// Construct a new path if there isn't one
|
||||||
if(!mPathFinder.isPathConstructed())
|
if(!mPathFinder.isPathConstructed())
|
||||||
|
@ -381,7 +320,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
storage.setState(Wander_Walking, true);
|
storage.setState(AiWanderStorage::Wander_Walking, true);
|
||||||
mHasDestination = true;
|
mHasDestination = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -410,26 +349,26 @@ namespace MWMechanics
|
||||||
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
storage.setState(Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos)
|
void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage, ESM::Position& pos)
|
||||||
{
|
{
|
||||||
switch (storage.mState)
|
switch (storage.mState)
|
||||||
{
|
{
|
||||||
case Wander_IdleNow:
|
case AiWanderStorage::Wander_IdleNow:
|
||||||
onIdleStatePerFrameActions(actor, duration, storage);
|
onIdleStatePerFrameActions(actor, duration, storage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Wander_Walking:
|
case AiWanderStorage::Wander_Walking:
|
||||||
onWalkingStatePerFrameActions(actor, duration, storage, pos);
|
onWalkingStatePerFrameActions(actor, duration, storage, pos);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Wander_ChooseAction:
|
case AiWanderStorage::Wander_ChooseAction:
|
||||||
onChooseActionStatePerFrameActions(actor, storage);
|
onChooseActionStatePerFrameActions(actor, storage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Wander_MoveNow:
|
case AiWanderStorage::Wander_MoveNow:
|
||||||
break; // nothing to do
|
break; // nothing to do
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -451,7 +390,7 @@ namespace MWMechanics
|
||||||
if (mDistance && // actor is not intended to be stationary
|
if (mDistance && // actor is not intended to be stationary
|
||||||
proximityToDoor(actor, distance*1.6f))
|
proximityToDoor(actor, distance*1.6f))
|
||||||
{
|
{
|
||||||
storage.setState(Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
storage.mTrimCurrentNode = false; // just in case
|
storage.mTrimCurrentNode = false; // just in case
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -468,13 +407,13 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if idle animation finished
|
// Check if idle animation finished
|
||||||
GreetingState& greetingState = storage.mSaidGreeting;
|
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
||||||
{
|
{
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
storage.setState(Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
else
|
else
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +424,7 @@ namespace MWMechanics
|
||||||
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))
|
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))
|
||||||
{
|
{
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -502,7 +441,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (!idleAnimation && mDistance)
|
if (!idleAnimation && mDistance)
|
||||||
{
|
{
|
||||||
storage.setState(Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(idleAnimation)
|
if(idleAnimation)
|
||||||
|
@ -512,13 +451,13 @@ namespace MWMechanics
|
||||||
if(!playIdle(actor, idleAnimation))
|
if(!playIdle(actor, idleAnimation))
|
||||||
{
|
{
|
||||||
storage.mBadIdles.push_back(idleAnimation);
|
storage.mBadIdles.push_back(idleAnimation);
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.setState(Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
||||||
|
@ -534,7 +473,7 @@ namespace MWMechanics
|
||||||
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_MoveNow);
|
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.mStuckCount++; // TODO: maybe no longer needed
|
storage.mStuckCount++; // TODO: maybe no longer needed
|
||||||
|
@ -545,7 +484,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
stopWalking(actor, storage);
|
stopWalking(actor, storage);
|
||||||
storage.setState(Wander_ChooseAction);
|
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||||
storage.mStuckCount = 0;
|
storage.mStuckCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,8 +535,8 @@ namespace MWMechanics
|
||||||
float playerDistSqr = (playerPos - actorPos).length2();
|
float playerDistSqr = (playerPos - actorPos).length2();
|
||||||
|
|
||||||
int& greetingTimer = storage.mGreetingTimer;
|
int& greetingTimer = storage.mGreetingTimer;
|
||||||
GreetingState& greetingState = storage.mSaidGreeting;
|
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||||
if (greetingState == Greet_None)
|
if (greetingState == AiWanderStorage::Greet_None)
|
||||||
{
|
{
|
||||||
if ((playerDistSqr <= helloDistance*helloDistance) &&
|
if ((playerDistSqr <= helloDistance*helloDistance) &&
|
||||||
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||||
|
@ -606,37 +545,37 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_START)
|
if (greetingTimer >= GREETING_SHOULD_START)
|
||||||
{
|
{
|
||||||
greetingState = Greet_InProgress;
|
greetingState = AiWanderStorage::Greet_InProgress;
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||||
greetingTimer = 0;
|
greetingTimer = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (greetingState == Greet_InProgress)
|
if (greetingState == AiWanderStorage::Greet_InProgress)
|
||||||
{
|
{
|
||||||
greetingTimer++;
|
greetingTimer++;
|
||||||
|
|
||||||
if (storage.mState == Wander_Walking)
|
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||||
{
|
{
|
||||||
stopWalking(actor, storage, false);
|
stopWalking(actor, storage, false);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
storage.setState(Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
turnActorToFacePlayer(actorPos, playerPos, storage);
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_END)
|
if (greetingTimer >= GREETING_SHOULD_END)
|
||||||
{
|
{
|
||||||
greetingState = Greet_Done;
|
greetingState = AiWanderStorage::Greet_Done;
|
||||||
greetingTimer = 0;
|
greetingTimer = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (greetingState == MWMechanics::AiWander::Greet_Done)
|
if (greetingState == AiWanderStorage::Greet_Done)
|
||||||
{
|
{
|
||||||
float resetDist = 2 * helloDistance;
|
float resetDist = 2 * helloDistance;
|
||||||
if (playerDistSqr >= resetDist*resetDist)
|
if (playerDistSqr >= resetDist*resetDist)
|
||||||
greetingState = Greet_None;
|
greetingState = AiWanderStorage::Greet_None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +615,7 @@ namespace MWMechanics
|
||||||
storage.mAllowedNodes.push_back(storage.mCurrentNode);
|
storage.mAllowedNodes.push_back(storage.mCurrentNode);
|
||||||
storage.mCurrentNode = temp;
|
storage.mCurrentNode = temp;
|
||||||
|
|
||||||
storage.setState(Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
}
|
}
|
||||||
// Choose a different node and delete this one from possible nodes because it is uncreachable:
|
// Choose a different node and delete this one from possible nodes because it is uncreachable:
|
||||||
else
|
else
|
||||||
|
|
|
@ -22,7 +22,80 @@ namespace ESM
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
struct AiWanderStorage;
|
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||||
|
struct AiWanderStorage : AiTemporaryBase
|
||||||
|
{
|
||||||
|
// the z rotation angle to reach
|
||||||
|
// when mTurnActorGivingGreetingToFacePlayer is true
|
||||||
|
float mTargetAngleRadians;
|
||||||
|
bool mTurnActorGivingGreetingToFacePlayer;
|
||||||
|
float mReaction; // update some actions infrequently
|
||||||
|
|
||||||
|
enum GreetingState
|
||||||
|
{
|
||||||
|
Greet_None,
|
||||||
|
Greet_InProgress,
|
||||||
|
Greet_Done
|
||||||
|
};
|
||||||
|
GreetingState mSaidGreeting;
|
||||||
|
int mGreetingTimer;
|
||||||
|
|
||||||
|
const MWWorld::CellStore* mCell; // for detecting cell change
|
||||||
|
|
||||||
|
// AiWander states
|
||||||
|
enum WanderState
|
||||||
|
{
|
||||||
|
Wander_ChooseAction,
|
||||||
|
Wander_IdleNow,
|
||||||
|
Wander_MoveNow,
|
||||||
|
Wander_Walking
|
||||||
|
};
|
||||||
|
WanderState mState;
|
||||||
|
|
||||||
|
bool mIsWanderingManually;
|
||||||
|
bool mCanWanderAlongPathGrid;
|
||||||
|
|
||||||
|
unsigned short mIdleAnimation;
|
||||||
|
std::vector<unsigned short> mBadIdles; // Idle animations that when called cause errors
|
||||||
|
|
||||||
|
// do we need to calculate allowed nodes based on mDistance
|
||||||
|
bool mPopulateAvailableNodes;
|
||||||
|
|
||||||
|
// allowed pathgrid nodes based on mDistance from the spawn point
|
||||||
|
// in local coordinates of mCell
|
||||||
|
std::vector<ESM::Pathgrid::Point> mAllowedNodes;
|
||||||
|
|
||||||
|
ESM::Pathgrid::Point mCurrentNode;
|
||||||
|
bool mTrimCurrentNode;
|
||||||
|
|
||||||
|
float mDoorCheckDuration;
|
||||||
|
int mStuckCount;
|
||||||
|
|
||||||
|
AiWanderStorage():
|
||||||
|
mTargetAngleRadians(0),
|
||||||
|
mTurnActorGivingGreetingToFacePlayer(false),
|
||||||
|
mReaction(0),
|
||||||
|
mSaidGreeting(Greet_None),
|
||||||
|
mGreetingTimer(0),
|
||||||
|
mCell(NULL),
|
||||||
|
mState(Wander_ChooseAction),
|
||||||
|
mIsWanderingManually(false),
|
||||||
|
mCanWanderAlongPathGrid(true),
|
||||||
|
mIdleAnimation(0),
|
||||||
|
mBadIdles(),
|
||||||
|
mPopulateAvailableNodes(true),
|
||||||
|
mAllowedNodes(),
|
||||||
|
mTrimCurrentNode(false),
|
||||||
|
mDoorCheckDuration(0), // TODO: maybe no longer needed
|
||||||
|
mStuckCount(0)
|
||||||
|
{};
|
||||||
|
|
||||||
|
void setState(const WanderState wanderState, const bool isManualWander = false)
|
||||||
|
{
|
||||||
|
mState = wanderState;
|
||||||
|
mIsWanderingManually = isManualWander;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Causes the Actor to wander within a specified range
|
/// \brief Causes the Actor to wander within a specified range
|
||||||
class AiWander : public AiPackage
|
class AiWander : public AiPackage
|
||||||
|
@ -52,19 +125,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
|
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const;
|
||||||
|
|
||||||
enum GreetingState {
|
|
||||||
Greet_None,
|
|
||||||
Greet_InProgress,
|
|
||||||
Greet_Done
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WanderState {
|
|
||||||
Wander_ChooseAction,
|
|
||||||
Wander_IdleNow,
|
|
||||||
Wander_MoveNow,
|
|
||||||
Wander_Walking
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NOTE: mDistance and mDuration must be set already
|
// NOTE: mDistance and mDuration must be set already
|
||||||
void init();
|
void init();
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
|
#include "aicombataction.hpp"
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
|
@ -370,6 +371,10 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
|
||||||
{
|
{
|
||||||
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
||||||
jumpAnimName = "jump";
|
jumpAnimName = "jump";
|
||||||
|
|
||||||
|
// For crossbow animations use 1h ones as fallback
|
||||||
|
if (mWeaponType == WeapType_Crossbow)
|
||||||
|
jumpAnimName += "1h";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,11 +423,18 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
||||||
movementAnimName = movestate->groupname;
|
movementAnimName = movestate->groupname;
|
||||||
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
||||||
{
|
{
|
||||||
|
if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case
|
||||||
|
movementAnimName = weap->shortgroup + movementAnimName;
|
||||||
|
else
|
||||||
movementAnimName += weap->shortgroup;
|
movementAnimName += weap->shortgroup;
|
||||||
if(!mAnimation->hasAnimation(movementAnimName))
|
if(!mAnimation->hasAnimation(movementAnimName))
|
||||||
{
|
{
|
||||||
movemask = MWRender::Animation::BlendMask_LowerBody;
|
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||||
movementAnimName = movestate->groupname;
|
movementAnimName = movestate->groupname;
|
||||||
|
|
||||||
|
// For crossbow animations use 1h ones as fallback
|
||||||
|
if (mWeaponType == WeapType_Crossbow)
|
||||||
|
movementAnimName += "1h";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,9 +471,11 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're playing the same animation, restart from the loop start instead of the
|
// If we're playing the same animation, start it from the point it ended
|
||||||
* beginning. */
|
bool sameAnim = (movementAnimName == mCurrentMovement);
|
||||||
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1);
|
float startPoint = 0.f;
|
||||||
|
if (sameAnim)
|
||||||
|
mAnimation->getInfo(mCurrentMovement, &startPoint);
|
||||||
|
|
||||||
mMovementAnimationControlled = true;
|
mMovementAnimationControlled = true;
|
||||||
|
|
||||||
|
@ -510,7 +524,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,
|
mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false,
|
||||||
1.f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul, true);
|
1.f, (!sameAnim ? "start" : "loop start"), "stop", startPoint, ~0ul, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -660,16 +674,19 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
|
||||||
|
|
||||||
void CharacterController::playDeath(float startpoint, CharacterState death)
|
void CharacterController::playDeath(float startpoint, CharacterState death)
|
||||||
{
|
{
|
||||||
|
// Make sure the character was swimming upon death for forward-compatibility
|
||||||
|
const bool wasSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
|
||||||
|
|
||||||
switch (death)
|
switch (death)
|
||||||
{
|
{
|
||||||
case CharState_SwimDeath:
|
case CharState_SwimDeath:
|
||||||
mCurrentDeath = "swimdeath";
|
mCurrentDeath = "swimdeath";
|
||||||
break;
|
break;
|
||||||
case CharState_SwimDeathKnockDown:
|
case CharState_SwimDeathKnockDown:
|
||||||
mCurrentDeath = "swimdeathknockdown";
|
mCurrentDeath = (wasSwimming ? "swimdeathknockdown" : "deathknockdown");
|
||||||
break;
|
break;
|
||||||
case CharState_SwimDeathKnockOut:
|
case CharState_SwimDeathKnockOut:
|
||||||
mCurrentDeath = "swimdeathknockout";
|
mCurrentDeath = (wasSwimming ? "swimdeathknockout" : "deathknockout");
|
||||||
break;
|
break;
|
||||||
case CharState_DeathKnockDown:
|
case CharState_DeathKnockDown:
|
||||||
mCurrentDeath = "deathknockdown";
|
mCurrentDeath = "deathknockdown";
|
||||||
|
@ -778,6 +795,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
, mSecondsOfRunning(0)
|
, mSecondsOfRunning(0)
|
||||||
, mTurnAnimationThreshold(0)
|
, mTurnAnimationThreshold(0)
|
||||||
, mAttackingOrSpell(false)
|
, mAttackingOrSpell(false)
|
||||||
|
, mCastingManualSpell(false)
|
||||||
, mTimeUntilWake(0.f)
|
, mTimeUntilWake(0.f)
|
||||||
{
|
{
|
||||||
if(!mAnimation)
|
if(!mAnimation)
|
||||||
|
@ -991,7 +1009,8 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
|
||||||
// the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type.
|
// the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type.
|
||||||
&& evt.compare(off, len, mAttackType + " release") == 0)
|
&& evt.compare(off, len, mAttackType + " release") == 0)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->castSpell(mPtr);
|
MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell);
|
||||||
|
mCastingManualSpell = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0)
|
else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0)
|
||||||
|
@ -1072,13 +1091,18 @@ bool CharacterController::updateCreatureState()
|
||||||
if (weapType == WeapType_Spell)
|
if (weapType == WeapType_Spell)
|
||||||
{
|
{
|
||||||
const std::string spellid = stats.getSpells().getSelectedSpell();
|
const std::string spellid = stats.getSpells().getSelectedSpell();
|
||||||
if (!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
|
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
|
||||||
|
|
||||||
|
if (!spellid.empty() && canCast)
|
||||||
{
|
{
|
||||||
MWMechanics::CastSpell cast(mPtr, NULL);
|
MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
|
||||||
cast.playSpellCastingEffects(spellid);
|
cast.playSpellCastingEffects(spellid);
|
||||||
|
|
||||||
if (!mAnimation->hasAnimation("spellcast"))
|
if (!mAnimation->hasAnimation("spellcast"))
|
||||||
MWBase::Environment::get().getWorld()->castSpell(mPtr); // No "release" text key to use, so cast immediately
|
{
|
||||||
|
MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately
|
||||||
|
mCastingManualSpell = false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid);
|
const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid);
|
||||||
|
@ -1204,6 +1228,10 @@ bool CharacterController::updateWeaponState()
|
||||||
if ((!isWerewolf || mWeaponType != WeapType_Spell)
|
if ((!isWerewolf || mWeaponType != WeapType_Spell)
|
||||||
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
||||||
&& !isStillWeapon)
|
&& !isStillWeapon)
|
||||||
|
{
|
||||||
|
// We can not play un-equip animation when we switch to HtH
|
||||||
|
// because we already un-equipped weapon
|
||||||
|
if (weaptype != WeapType_HandToHand || mWeaponType == WeapType_Spell)
|
||||||
{
|
{
|
||||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||||
getWeaponGroup(mWeaponType, weapgroup);
|
getWeaponGroup(mWeaponType, weapgroup);
|
||||||
|
@ -1211,6 +1239,7 @@ bool CharacterController::updateWeaponState()
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||||
|
}
|
||||||
|
|
||||||
if(!downSoundId.empty())
|
if(!downSoundId.empty())
|
||||||
{
|
{
|
||||||
|
@ -1351,10 +1380,11 @@ bool CharacterController::updateWeaponState()
|
||||||
stats.getSpells().setSelectedSpell(selectedSpell);
|
stats.getSpells().setSelectedSpell(selectedSpell);
|
||||||
}
|
}
|
||||||
std::string spellid = stats.getSpells().getSelectedSpell();
|
std::string spellid = stats.getSpells().getSelectedSpell();
|
||||||
|
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
|
||||||
|
|
||||||
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
|
if(!spellid.empty() && canCast)
|
||||||
{
|
{
|
||||||
MWMechanics::CastSpell cast(mPtr, NULL);
|
MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
|
||||||
cast.playSpellCastingEffects(spellid);
|
cast.playSpellCastingEffects(spellid);
|
||||||
|
|
||||||
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
||||||
|
@ -1622,16 +1652,18 @@ bool CharacterController::updateWeaponState()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: apply reload animations only for upper body since blending with movement animations can give weird result.
|
||||||
|
// Especially noticable with crossbow reload animation.
|
||||||
if(!start.empty())
|
if(!start.empty())
|
||||||
{
|
{
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_All, true,
|
MWRender::Animation::BlendMask_UpperBody, true,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
else
|
else
|
||||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_UpperBody, false,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2141,7 +2173,7 @@ void CharacterController::unpersistAnimationState()
|
||||||
|
|
||||||
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
|
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
|
||||||
mAnimation->play(anim.mGroup,
|
mAnimation->play(anim.mGroup,
|
||||||
Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f,
|
Priority_Persistent, MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||||
"start", "stop", complete, anim.mLoopCount, loopfallback);
|
"start", "stop", complete, anim.mLoopCount, loopfallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2359,6 +2391,11 @@ bool CharacterController::isAttackPrepairing() const
|
||||||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CharacterController::isCastingSpell() const
|
||||||
|
{
|
||||||
|
return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;
|
||||||
|
}
|
||||||
|
|
||||||
bool CharacterController::isReadyToBlock() const
|
bool CharacterController::isReadyToBlock() const
|
||||||
{
|
{
|
||||||
return updateCarriedLeftVisible(mWeaponType);
|
return updateCarriedLeftVisible(mWeaponType);
|
||||||
|
@ -2422,6 +2459,14 @@ void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
|
||||||
mAttackingOrSpell = attackingOrSpell;
|
mAttackingOrSpell = attackingOrSpell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterController::castSpell(const std::string spellId, bool manualSpell)
|
||||||
|
{
|
||||||
|
mAttackingOrSpell = true;
|
||||||
|
mCastingManualSpell = manualSpell;
|
||||||
|
ActionSpell action = ActionSpell(spellId);
|
||||||
|
action.prepare(mPtr);
|
||||||
|
}
|
||||||
|
|
||||||
void CharacterController::setAIAttackType(const std::string& attackType)
|
void CharacterController::setAIAttackType(const std::string& attackType)
|
||||||
{
|
{
|
||||||
mAttackType = attackType;
|
mAttackType = attackType;
|
||||||
|
|
|
@ -204,6 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
std::string mAttackType; // slash, chop or thrust
|
std::string mAttackType; // slash, chop or thrust
|
||||||
|
|
||||||
bool mAttackingOrSpell;
|
bool mAttackingOrSpell;
|
||||||
|
bool mCastingManualSpell;
|
||||||
|
|
||||||
float mTimeUntilWake;
|
float mTimeUntilWake;
|
||||||
|
|
||||||
|
@ -276,6 +277,7 @@ public:
|
||||||
void forceStateUpdate();
|
void forceStateUpdate();
|
||||||
|
|
||||||
bool isAttackPrepairing() const;
|
bool isAttackPrepairing() const;
|
||||||
|
bool isCastingSpell() const;
|
||||||
bool isReadyToBlock() const;
|
bool isReadyToBlock() const;
|
||||||
bool isKnockedDown() const;
|
bool isKnockedDown() const;
|
||||||
bool isKnockedOut() const;
|
bool isKnockedOut() const;
|
||||||
|
@ -286,6 +288,7 @@ public:
|
||||||
bool isAttackingOrSpell() const;
|
bool isAttackingOrSpell() const;
|
||||||
|
|
||||||
void setAttackingOrSpell(bool attackingOrSpell);
|
void setAttackingOrSpell(bool attackingOrSpell);
|
||||||
|
void castSpell(const std::string spellId, bool manualSpell=false);
|
||||||
void setAIAttackType(const std::string& attackType);
|
void setAIAttackType(const std::string& attackType);
|
||||||
static void setAttackTypeRandomly(std::string& attackType);
|
static void setAttackTypeRandomly(std::string& attackType);
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,8 @@ namespace MWMechanics
|
||||||
x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x));
|
x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x));
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() < x)
|
if (Misc::Rng::roll0to99() < x)
|
||||||
|
{
|
||||||
|
if (!(weapon.isEmpty() && !attacker.getClass().isNpc())) // Unarmed creature attacks don't affect armor condition
|
||||||
{
|
{
|
||||||
// Reduce shield durability by incoming damage
|
// Reduce shield durability by incoming damage
|
||||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||||
|
@ -122,7 +124,7 @@ namespace MWMechanics
|
||||||
shield->getCellRef().setCharge(shieldhealth);
|
shield->getCellRef().setCharge(shieldhealth);
|
||||||
if (shieldhealth == 0)
|
if (shieldhealth == 0)
|
||||||
inv.unequipItem(*shield, blocker);
|
inv.unequipItem(*shield, blocker);
|
||||||
|
}
|
||||||
// Reduce blocker fatigue
|
// Reduce blocker fatigue
|
||||||
const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat();
|
const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat();
|
||||||
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();
|
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace MWMechanics
|
||||||
int endurance = getAttribute(ESM::Attribute::Endurance).getModified();
|
int endurance = getAttribute(ESM::Attribute::Endurance).getModified();
|
||||||
DynamicStat<float> fatigue = getFatigue();
|
DynamicStat<float> fatigue = getFatigue();
|
||||||
float diff = (strength+willpower+agility+endurance) - fatigue.getBase();
|
float diff = (strength+willpower+agility+endurance) - fatigue.getBase();
|
||||||
float currentToBaseRatio = (fatigue.getCurrent() / fatigue.getBase());
|
float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0;
|
||||||
fatigue.setModified(fatigue.getModified() + diff, 0);
|
fatigue.setModified(fatigue.getModified() + diff, 0);
|
||||||
fatigue.setCurrent(fatigue.getBase() * currentToBaseRatio);
|
fatigue.setCurrent(fatigue.getBase() * currentToBaseRatio);
|
||||||
setFatigue(fatigue);
|
setFatigue(fatigue);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue