mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:09:40 +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
|
||||
# can use verions as well, like gcc:5.2
|
||||
# see https://hub.docker.com/_/gcc/
|
||||
image: gcc
|
||||
|
||||
cache:
|
||||
key: apt-cache
|
||||
paths:
|
||||
- apt-cache/
|
||||
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- 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 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/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/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
|
||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
||||
|
||||
build:
|
||||
stages:
|
||||
- build
|
||||
|
||||
Debian:
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: gcc
|
||||
cache:
|
||||
key: apt-cache
|
||||
paths:
|
||||
- apt-cache/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- 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 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/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/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
|
||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
||||
stage: build
|
||||
script:
|
||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
||||
|
@ -30,13 +30,40 @@ build:
|
|||
artifacts:
|
||||
paths:
|
||||
- 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:
|
||||
paths:
|
||||
- "*.o"
|
||||
|
||||
# TODO: run tests using the binary built before
|
||||
#test:
|
||||
# stage: test
|
||||
# script:
|
||||
# - ls
|
||||
- deps
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.zip"
|
||||
|
|
|
@ -15,7 +15,6 @@ env:
|
|||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
||||
- macos_qt_formula=qt
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
|
|
|
@ -22,6 +22,7 @@ Programmers
|
|||
alexanderkjall
|
||||
Alexander Nadeau (wareya)
|
||||
Alexander Olofsson (Ace)
|
||||
Alex S (docwest)
|
||||
Allofich
|
||||
Andrei Kortunov (akortunov)
|
||||
AnyOldName3
|
||||
|
@ -63,6 +64,7 @@ Programmers
|
|||
Evgeniy Mineev (sandstranger)
|
||||
Federico Guerra (FedeWar)
|
||||
Fil Krynicki (filkry)
|
||||
Finbar Crago (finbar-crago)
|
||||
Florian Weber (Florianjw)
|
||||
Gašper Sedej
|
||||
gugus/gus
|
||||
|
@ -141,6 +143,7 @@ Programmers
|
|||
Rohit Nirmal
|
||||
Roman Melnik (Kromgart)
|
||||
Roman Proskuryakov (kpp)
|
||||
Roman Siromakha (elsid)
|
||||
Sandy Carter (bwrsandman)
|
||||
Scott Howard
|
||||
scrawl
|
||||
|
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -2,14 +2,18 @@
|
|||
------
|
||||
|
||||
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 #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 #2626: Resurrecting the player does not resume the game
|
||||
Bug #2772: Non-existing class or faction freezes the game
|
||||
Bug #2835: Player able to slowly move when overencumbered
|
||||
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 #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 #3486: [Mod] NPC Commands does not work
|
||||
Bug #3591: Angled hit distance too low
|
||||
|
@ -17,19 +21,24 @@
|
|||
Bug #3876: Landscape texture painting is misaligned
|
||||
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 #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
|
||||
Bug #3993: Terrain texture blending map is not upscaled
|
||||
Bug #3997: Almalexia doesn't pace
|
||||
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 #4110: Fixed undo / redo menu text losing the assigned shortcuts
|
||||
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 #4230: AiTravel package issues break some Tribunal quests
|
||||
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 #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 #4307: World cleanup should remove dead bodies only if death animation is finished
|
||||
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 #4393: NPCs walk back to where they were after using ResetActors
|
||||
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 #4458: AiWander console command handles idle chances incorrectly
|
||||
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 #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
|
||||
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 #3641: Editor: Limit FPS in 3d preview window
|
||||
Feature #4222: 360° screenshots
|
||||
Feature #4256: Implement ToggleBorders (TB) console command
|
||||
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 #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
|
||||
Feature #4444: Per-group KF-animation files support
|
||||
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
|
||||
------
|
||||
|
|
|
@ -4,7 +4,7 @@ brew update
|
|||
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
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
|
||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
|
||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash
|
||||
# set -x # turn-on for debugging
|
||||
|
||||
MISSINGTOOLS=0
|
||||
|
||||
|
@ -76,7 +77,6 @@ while [ $# -gt 0 ]; do
|
|||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvV]
|
||||
|
||||
Options:
|
||||
-c <Release/Debug>
|
||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||
|
@ -232,10 +232,9 @@ fi
|
|||
case $VS_VERSION in
|
||||
15|15.0|2017 )
|
||||
GENERATOR="Visual Studio 15 2017"
|
||||
TOOLSET="vc140"
|
||||
TOOLSET_REAL="vc141"
|
||||
TOOLSET="vc141"
|
||||
MSVC_REAL_VER="15"
|
||||
MSVC_VER="14"
|
||||
MSVC_VER="14.1"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2017"
|
||||
;;
|
||||
|
@ -243,9 +242,8 @@ case $VS_VERSION in
|
|||
14|14.0|2015 )
|
||||
GENERATOR="Visual Studio 14 2015"
|
||||
TOOLSET="vc140"
|
||||
TOOLSET_REAL="vc140"
|
||||
MSVC_REAL_VER="14"
|
||||
MSVC_VER="14"
|
||||
MSVC_VER="14.0"
|
||||
MSVC_YEAR="2015"
|
||||
MSVC_DISPLAY_YEAR="2015"
|
||||
;;
|
||||
|
@ -253,9 +251,8 @@ case $VS_VERSION in
|
|||
12|12.0|2013 )
|
||||
GENERATOR="Visual Studio 12 2013"
|
||||
TOOLSET="vc120"
|
||||
TOOLSET_REAL="vc120"
|
||||
MSVC_REAL_VER="12"
|
||||
MSVC_VER="12"
|
||||
MSVC_VER="12.0"
|
||||
MSVC_YEAR="2013"
|
||||
MSVC_DISPLAY_YEAR="2013"
|
||||
;;
|
||||
|
@ -325,9 +322,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
download "Boost 1.61.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-${MSVC_VER}.0-${BITS}.exe" \
|
||||
"boost-1.61.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||
download "Boost 1.67.0" \
|
||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
||||
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
||||
fi
|
||||
|
||||
# Bullet
|
||||
|
@ -365,8 +362,8 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
QT_SUFFIX=""
|
||||
fi
|
||||
|
||||
download "Qt 5.7.2" \
|
||||
"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" \
|
||||
download "Qt 5.7.0" \
|
||||
"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" \
|
||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
||||
"qt-5-install.qs"
|
||||
|
@ -403,9 +400,9 @@ echo
|
|||
|
||||
# Boost
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Boost 1.61.0... "
|
||||
printf "Boost 1.67.0... "
|
||||
else
|
||||
if [ $MSVC_VER -eq 12 ]; then
|
||||
if [ $MSVC_VER -eq 12.0 ]; then
|
||||
printf "Boost 1.58.0 AppVeyor... "
|
||||
else
|
||||
printf "Boost 1.67.0 AppVeyor... "
|
||||
|
@ -417,17 +414,28 @@ fi
|
|||
|
||||
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. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
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
|
||||
|
||||
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}"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
# Appveyor unstable has all the boost we need already
|
||||
|
@ -444,19 +452,17 @@ fi
|
|||
|
||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||
-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.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# Bullet
|
||||
printf "Bullet 2.86... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d Bullet ]; then
|
||||
printf -- "Exists. (No version checking) "
|
||||
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
|
||||
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
||||
fi
|
||||
|
||||
export BULLET_ROOT="$(real_pwd)/Bullet"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# FFmpeg
|
||||
printf "FFmpeg 3.2.4... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
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-dev-win${BITS}.zip" $STRIP
|
||||
|
||||
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
||||
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
||||
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
||||
fi
|
||||
|
||||
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
||||
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
||||
|
||||
if [ $BITS -eq 32 ]; then
|
||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||
fi
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# MyGUI
|
||||
printf "MyGUI 3.2.2... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d MyGUI ] && \
|
||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||
|
@ -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
|
||||
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
||||
fi
|
||||
|
||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="_d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# OpenAL
|
||||
printf "OpenAL-Soft 1.17.2... "
|
||||
{
|
||||
|
@ -542,24 +533,18 @@ printf "OpenAL-Soft 1.17.2... "
|
|||
rm -rf openal-soft-1.17.2-bin
|
||||
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
||||
fi
|
||||
|
||||
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
||||
|
||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
||||
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
||||
|
||||
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# OSG
|
||||
printf "OSG 3.4.1-scrawl... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
|
||||
if [ -d OSG ] && \
|
||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" 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
|
||||
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
||||
fi
|
||||
|
||||
OSG_SDK="$(real_pwd)/OSG"
|
||||
|
||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${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_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
|
||||
|
||||
echo Done.
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# Qt
|
||||
if [ -z $APPVEYOR ]; then
|
||||
printf "Qt 5.7.0... "
|
||||
|
@ -605,71 +583,53 @@ fi
|
|||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
if [ -z $APPVEYOR ]; then
|
||||
cd $DEPS_INSTALL
|
||||
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
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf Qt
|
||||
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
||||
|
||||
|
||||
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
||||
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
||||
|
||||
printf -- "(Installation might take a while) "
|
||||
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
||||
|
||||
mv qt-install.qs Qt/
|
||||
|
||||
echo Done.
|
||||
printf " Cleaning up extraneous data... "
|
||||
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
|
||||
fi
|
||||
|
||||
cd $QT_SDK
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
else
|
||||
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
||||
|
||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||
|
||||
if [ $CONFIGURATION == "Debug" ]; then
|
||||
SUFFIX="d"
|
||||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
|
||||
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||
|
||||
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
||||
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
||||
|
||||
echo Done.
|
||||
fi
|
||||
}
|
||||
cd $DEPS
|
||||
echo
|
||||
|
||||
# SDL2
|
||||
printf "SDL 2.0.7... "
|
||||
{
|
||||
|
@ -679,26 +639,18 @@ printf "SDL 2.0.7... "
|
|||
rm -rf SDL2-2.0.7
|
||||
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
||||
fi
|
||||
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
||||
|
||||
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
|
||||
echo Done.
|
||||
}
|
||||
echo
|
||||
|
||||
|
||||
cd $DEPS_INSTALL/..
|
||||
|
||||
echo
|
||||
echo "Setting up OpenMW build..."
|
||||
|
||||
add_cmake_opts -DBUILD_BSATOOL=no \
|
||||
-DBUILD_ESMTOOL=no \
|
||||
-DBUILD_MYGUI_PLUGIN=no \
|
||||
-DOPENMW_MP_BUILD=on
|
||||
|
||||
if [ ! -z $CI ]; then
|
||||
case $STEP in
|
||||
components )
|
||||
|
@ -710,7 +662,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
openmw )
|
||||
echo " Building subproject: OpenMW."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
|
@ -719,7 +670,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENCS=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
opencs )
|
||||
echo " Building subproject: OpenCS."
|
||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||
|
@ -728,7 +678,6 @@ if [ ! -z $CI ]; then
|
|||
-DBUILD_OPENMW=no \
|
||||
-DBUILD_WIZARD=no
|
||||
;;
|
||||
|
||||
misc )
|
||||
echo " Building subprojects: Misc."
|
||||
add_cmake_opts -DBUILD_OPENCS=no \
|
||||
|
@ -736,7 +685,6 @@ if [ ! -z $CI ]; then
|
|||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# NOTE: Disable this when/if we want to run test cases
|
||||
#if [ -z $CI ]; then
|
||||
echo "- Copying Runtime DLLs..."
|
||||
|
@ -745,16 +693,13 @@ fi
|
|||
TARGET="$(basename "$DLL")"
|
||||
if [[ "$DLL" == *":"* ]]; then
|
||||
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
||||
|
||||
DLL=${SPLIT[0]}
|
||||
TARGET=${SPLIT[1]}
|
||||
fi
|
||||
|
||||
echo " ${TARGET}."
|
||||
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
||||
done
|
||||
echo
|
||||
|
||||
echo "- OSG Plugin DLLs..."
|
||||
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
for DLL in $OSG_PLUGINS; do
|
||||
|
@ -762,7 +707,6 @@ fi
|
|||
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
||||
done
|
||||
echo
|
||||
|
||||
echo "- Qt Platform DLLs..."
|
||||
mkdir -p ${BUILD_CONFIG}/platforms
|
||||
for DLL in $QT_PLATFORMS; do
|
||||
|
@ -771,16 +715,13 @@ fi
|
|||
done
|
||||
echo
|
||||
#fi
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
printf -- "- Configuring... "
|
||||
else
|
||||
echo "- cmake .. $CMAKE_OPTS"
|
||||
fi
|
||||
|
||||
run_cmd cmake .. $CMAKE_OPTS
|
||||
RET=$?
|
||||
|
||||
if [ -z $VERBOSE ]; then
|
||||
if [ $RET -eq 0 ]; then
|
||||
echo Done.
|
||||
|
@ -788,5 +729,4 @@ if [ -z $VERBOSE ]; then
|
|||
echo Failed.
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $RET
|
||||
|
|
|
@ -4,7 +4,7 @@ export CXX=clang++
|
|||
export CC=clang
|
||||
|
||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||
QT_PATH=`brew --prefix $macos_qt_formula`
|
||||
QT_PATH=`brew --prefix qt`
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
|
|
|
@ -610,10 +610,10 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
# Release builds use the debug console
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
|
||||
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
|
||||
# Release builds don't use the debug console
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS")
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||
endif()
|
||||
|
||||
# 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
|
||||
# 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
|
||||
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
|
||||
4347 # Non-template function with same name and parameter count as template function
|
||||
|
@ -645,6 +646,7 @@ if (WIN32)
|
|||
|
||||
# caused by MyGUI
|
||||
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
|
||||
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:
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
|||
QDir::currentPath(),
|
||||
QString(tr("Text file (*.txt)")));
|
||||
|
||||
|
||||
if (scriptFile.isEmpty())
|
||||
return;
|
||||
|
||||
|
@ -53,7 +52,7 @@ void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
|||
return;
|
||||
|
||||
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
||||
|
||||
runScriptAfterStartupField->setText(path);
|
||||
}
|
||||
|
||||
bool Launcher::AdvancedPage::loadSettings()
|
||||
|
@ -73,18 +72,8 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "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");
|
||||
|
||||
// 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
|
||||
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
||||
|
@ -94,6 +83,16 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(timePlayedCheckbox, "timeplayed", "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
|
||||
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
|
||||
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
|
||||
|
@ -125,16 +124,8 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
||||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "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");
|
||||
|
||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
||||
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
||||
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
||||
|
||||
// Input Settings
|
||||
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
||||
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
||||
|
@ -147,6 +138,15 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
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
|
||||
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
||||
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
|
||||
|
@ -167,4 +167,4 @@ void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::str
|
|||
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
|
||||
{
|
||||
loadCellsForAutocomplete(cellNames);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,19 +90,19 @@ void Launcher::MainDialog::createIcons()
|
|||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||
graphicsButton->setIcon(QIcon::fromTheme("video-display"));
|
||||
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
|
||||
graphicsButton->setText(tr("Graphics"));
|
||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
||||
settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
|
||||
settingsButton->setIcon(QIcon(":/images/preferences.png"));
|
||||
settingsButton->setText(tr("Settings"));
|
||||
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||
|
||||
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
||||
advancedButton->setIcon(QIcon::fromTheme("emblem-system"));
|
||||
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
|
||||
advancedButton->setText(tr("Advanced"));
|
||||
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
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::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
std::time_t defaultTime = 0;
|
||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||
|
||||
|
@ -890,7 +889,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
|||
multistrmap::const_iterator it = ini.begin();
|
||||
for (int i=0; it != ini.end(); i++)
|
||||
{
|
||||
gameFile = baseGameFile;
|
||||
std::string gameFile = baseGameFile;
|
||||
gameFile.append(std::to_string(i));
|
||||
|
||||
it = ini.find(gameFile);
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
||||
#include <components/nifosg/nifloader.hpp>
|
||||
|
@ -18,12 +21,16 @@
|
|||
|
||||
using namespace Fallback;
|
||||
|
||||
CS::Editor::Editor ()
|
||||
CS::Editor::Editor (int argc, char **argv)
|
||||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||
mViewManager (mDocumentManager), mPid(""),
|
||||
mLock(), mMerge (mDocumentManager),
|
||||
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();
|
||||
|
||||
setupDataFiles (config.first);
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace CS
|
|||
|
||||
public:
|
||||
|
||||
Editor ();
|
||||
Editor (int argc, char **argv);
|
||||
~Editor ();
|
||||
|
||||
bool makeIPCServer();
|
||||
|
|
|
@ -48,7 +48,7 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
// To allow background thread drawing in OSG
|
||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
||||
|
||||
|
@ -67,7 +67,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||
|
||||
CS::Editor editor;
|
||||
CS::Editor editor(argc, argv);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
declareBool ("camera-ortho", "Orthographic projection for camera", false);
|
||||
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).
|
||||
|
|
|
@ -22,7 +22,8 @@ void CSMTools::Search::searchTextCell (const CSMWorld::IdTableBase *model,
|
|||
|
||||
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;
|
||||
hint
|
||||
|
@ -120,25 +121,26 @@ QString CSMTools::Search::flatten (const QString& text) const
|
|||
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) {}
|
||||
|
||||
CSMTools::Search::Search (Type type, const std::string& value)
|
||||
: mType (type), mText (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, const std::string& value)
|
||||
: mType (type), mText (value), mValue (0), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
{
|
||||
if (type!=Type_Text && type!=Type_Id)
|
||||
throw std::logic_error ("Invalid search parameter (string)");
|
||||
}
|
||||
|
||||
CSMTools::Search::Search (Type type, const QRegExp& value)
|
||||
: mType (type), mRegExp (value), mValue (0), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, const QRegExp& value)
|
||||
: 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)
|
||||
throw std::logic_error ("Invalid search parameter (RegExp)");
|
||||
}
|
||||
|
||||
CSMTools::Search::Search (Type type, int value)
|
||||
: mType (type), mValue (value), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
CSMTools::Search::Search (Type type, bool caseSensitive, int value)
|
||||
: mType (type), mValue (value), mCase (caseSensitive), mIdColumn (0), mTypeColumn (0), mPaddingBefore (10), mPaddingAfter (10)
|
||||
{
|
||||
if (type!=Type_RecordState)
|
||||
throw std::logic_error ("invalid search parameter (int)");
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace CSMTools
|
|||
std::string mText;
|
||||
QRegExp mRegExp;
|
||||
int mValue;
|
||||
bool mCase;
|
||||
std::set<int> mColumns;
|
||||
int mIdColumn;
|
||||
int mTypeColumn;
|
||||
|
@ -67,11 +68,11 @@ namespace CSMTools
|
|||
|
||||
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.
|
||||
void configure (const CSMWorld::IdTableBase *model);
|
||||
|
|
|
@ -136,7 +136,7 @@ namespace CSMWorld
|
|||
struct VarTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
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
|
||||
|
@ -161,7 +161,7 @@ namespace CSMWorld
|
|||
template<typename 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
|
||||
{
|
||||
|
|
|
@ -84,15 +84,28 @@ bool CSMWorld::IdTable::setData (const QModelIndex &index, const QVariant &value
|
|||
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
|
||||
{
|
||||
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);
|
||||
if (stateColumn != -1)
|
||||
{
|
||||
QModelIndex stateIndex = this->index(index.row(), stateColumn);
|
||||
emit dataChanged(stateIndex, stateIndex);
|
||||
}
|
||||
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);
|
||||
emit dataChanged(stateIndex, stateIndex);
|
||||
}
|
||||
} else
|
||||
emit dataChanged(index, index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -156,4 +156,4 @@ namespace CSMWorld
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -101,15 +101,39 @@ void CSVDoc::View::setupFileMenu()
|
|||
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()
|
||||
{
|
||||
QMenu *edit = menuBar()->addMenu (tr ("Edit"));
|
||||
|
||||
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo"));
|
||||
setupShortcut("document-edit-undo", mUndo);
|
||||
connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ()));
|
||||
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);
|
||||
edit->addAction (mRedo);
|
||||
|
||||
|
@ -622,6 +646,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
|
|||
}
|
||||
assert(view);
|
||||
view->setParent(this);
|
||||
view->setEditLock (mDocument->getState() & CSMDoc::State_Locked);
|
||||
mSubViews.append(view); // only after assert
|
||||
|
||||
int minWidth = windows["minimum-width"].toInt();
|
||||
|
|
|
@ -152,6 +152,10 @@ namespace CSVDoc
|
|||
|
||||
void settingChanged (const CSMPrefs::Setting *setting);
|
||||
|
||||
void undoActionChanged();
|
||||
|
||||
void redoActionChanged();
|
||||
|
||||
void newView();
|
||||
|
||||
void save();
|
||||
|
|
|
@ -102,6 +102,14 @@ void CSVRender::Object::update()
|
|||
if (recordType == CSMWorld::UniversalId::Type_Light)
|
||||
{
|
||||
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())
|
||||
|
@ -372,10 +380,10 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
|
|||
indices[j] -= VertexCount;
|
||||
}
|
||||
|
||||
size_t offset = i * IndicesPerSegment;
|
||||
size_t elementOffset = i * IndicesPerSegment;
|
||||
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()) );
|
||||
mTimer.start( 10 );
|
||||
|
||||
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
|
||||
setRunMaxFrameRate(frameRateLimit);
|
||||
}
|
||||
|
||||
CompositeViewer &CompositeViewer::get()
|
||||
|
@ -168,6 +171,12 @@ void CompositeViewer::update()
|
|||
|
||||
mSimulationTime += dt;
|
||||
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());
|
||||
}
|
||||
else if (*setting=="Rendering/framerate-limit")
|
||||
{
|
||||
CompositeViewer::get().setRunMaxFrameRate(setting->toInt());
|
||||
}
|
||||
else if (*setting=="Rendering/camera-fov" ||
|
||||
*setting=="Rendering/camera-ortho" ||
|
||||
*setting=="Rendering/camera-ortho-size")
|
||||
|
|
|
@ -651,7 +651,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
|
|||
|
||||
if (mDragMode == InteractionType_PrimaryEdit)
|
||||
{
|
||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
|
||||
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)
|
||||
: QWidget (parent), mSearch ("Search"), mSearchEnabled (false), mReplace ("Replace All")
|
||||
: QWidget (parent), mSearch (tr("Search")), mSearchEnabled (false), mReplace (tr("Replace All"))
|
||||
{
|
||||
mLayout = new QGridLayout (this);
|
||||
|
||||
|
@ -48,29 +48,26 @@ CSVTools::SearchBox::SearchBox (QWidget *parent)
|
|||
++iter)
|
||||
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
|
||||
|
||||
mMode.addItem ("Text");
|
||||
mMode.addItem ("Text (RegEx)");
|
||||
mMode.addItem ("ID");
|
||||
mMode.addItem ("ID (RegEx)");
|
||||
mMode.addItem ("Record State");
|
||||
|
||||
mMode.addItem (tr("Text"));
|
||||
mMode.addItem (tr("Text (RegEx)"));
|
||||
mMode.addItem (tr("ID"));
|
||||
mMode.addItem (tr("ID (RegEx)"));
|
||||
mMode.addItem (tr("Record State"));
|
||||
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
||||
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 (1, &mRecordState);
|
||||
mLayout->addWidget (&mInput, 0, 1);
|
||||
|
||||
mLayout->addWidget (&mInput, 0, 1);
|
||||
|
||||
connect (&mMode, SIGNAL (activated (int)), this, SLOT (modeSelected (int)));
|
||||
|
||||
connect (&mText, SIGNAL (textChanged (const QString&)),
|
||||
this, SLOT (textChanged (const QString&)));
|
||||
mCaseSensitive.setText (tr ("Case"));
|
||||
mLayout->addWidget (&mCaseSensitive, 0, 2);
|
||||
|
||||
connect (&mSearch, SIGNAL (clicked (bool)), this, SLOT (startSearch (bool)));
|
||||
|
||||
connect (&mText, SIGNAL (returnPressed()), this, SLOT (startSearch()));
|
||||
mLayout->addWidget (&mSearch, 0, 3);
|
||||
|
||||
// replace panel
|
||||
mReplaceInput.insertWidget (0, &mReplaceText);
|
||||
|
@ -102,23 +99,24 @@ void CSVTools::SearchBox::setSearchMode (bool enabled)
|
|||
|
||||
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)
|
||||
{
|
||||
case CSMTools::Search::Type_Text:
|
||||
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_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:
|
||||
|
||||
return CSMTools::Search (type, mRecordState.currentIndex());
|
||||
return CSMTools::Search (type, caseSensitive, mRecordState.currentIndex());
|
||||
|
||||
case CSMTools::Search::Type_None:
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QWidget>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
#include <QStackedWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
|
@ -24,6 +25,7 @@ namespace CSVTools
|
|||
QStackedWidget mInput;
|
||||
QLineEdit mText;
|
||||
QComboBox mRecordState;
|
||||
QCheckBox mCaseSensitive;
|
||||
QPushButton mSearch;
|
||||
QGridLayout *mLayout;
|
||||
QComboBox mMode;
|
||||
|
|
|
@ -32,7 +32,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps ()
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -142,9 +142,23 @@ void CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *set
|
|||
|
||||
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);
|
||||
|
||||
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 (
|
||||
|
|
|
@ -11,11 +11,18 @@ namespace CSMPrefs
|
|||
|
||||
namespace CSVWorld
|
||||
{
|
||||
struct Icon
|
||||
{
|
||||
int mValue;
|
||||
QIcon mIcon;
|
||||
QString mName;
|
||||
};
|
||||
|
||||
class DataDisplayDelegate : public EnumDelegate
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector < std::pair < int, QIcon > > IconList;
|
||||
typedef std::vector<Icon> IconList;
|
||||
typedef std::vector<std::pair<int, QString> > ValueList;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -110,7 +110,11 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
|
|||
int valueIndex = getValueIndex(index);
|
||||
if (valueIndex != -1)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
|
||||
QStyleOptionViewItem itemOption(option);
|
||||
#else
|
||||
QStyleOptionViewItemV4 itemOption(option);
|
||||
#endif
|
||||
itemOption.text = mValues.at(valueIndex).second;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT ANDROID)
|
||||
set(GAME ${GAME} crashcatcher.cpp)
|
||||
endif()
|
||||
set(GAME_HEADER
|
||||
engine.hpp
|
||||
)
|
||||
|
||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
|
@ -83,9 +81,9 @@ add_openmw_dir (mwclass
|
|||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||
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
|
||||
character actors objects aistate coordinateconverter trading aiface weaponpriority spellpriority
|
||||
character actors objects aistate coordinateconverter trading weaponpriority spellpriority
|
||||
)
|
||||
|
||||
add_openmw_dir (mwstate
|
||||
|
|
|
@ -381,33 +381,24 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
|||
setWindowIcon();
|
||||
|
||||
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_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->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
||||
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->depth = 24;
|
||||
traits->stencil = 8;
|
||||
traits->vsync = vsync;
|
||||
traits->doubleBuffer = true;
|
||||
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
||||
|
||||
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;
|
||||
|
||||
mViewer = new osgViewer::Viewer;
|
||||
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);
|
||||
|
||||
// Load settings
|
||||
Settings::Manager settings;
|
||||
std::string settingspath;
|
||||
|
||||
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(),
|
||||
Settings::Manager::getString("screenshot format", "General"));
|
||||
|
||||
|
@ -677,12 +659,24 @@ void OMW::Engine::go()
|
|||
|
||||
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||
|
||||
// Create encoder
|
||||
ToUTF8::Utf8Encoder encoder (mEncoding);
|
||||
mEncoder = &encoder;
|
||||
|
||||
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())
|
||||
{
|
||||
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/crashcatcher/crashcatcher.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/escape.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
@ -24,18 +25,6 @@
|
|||
#include <unistd.h>
|
||||
#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
|
||||
*/
|
||||
|
@ -339,18 +328,7 @@ int main(int argc, char**argv)
|
|||
std::cerr.rdbuf (&cerrsb);
|
||||
#endif
|
||||
|
||||
|
||||
#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
|
||||
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
|
||||
|
||||
#ifdef __APPLE__
|
||||
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
|
||||
virtual void keepPlayerAlive() = 0;
|
||||
|
||||
virtual bool isCastingSpell (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 void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;
|
||||
|
||||
/// 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
|
||||
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;
|
||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
|||
|
||||
virtual void endGame() = 0;
|
||||
|
||||
virtual void resumeGame() = 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;
|
||||
|
|
|
@ -146,7 +146,7 @@ namespace MWBase
|
|||
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
||||
|
||||
/// 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;
|
||||
|
||||
|
|
|
@ -297,7 +297,7 @@ namespace MWBase
|
|||
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
||||
/// 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.
|
||||
|
||||
virtual bool toggleCollisionMode() = 0;
|
||||
|
@ -487,7 +487,7 @@ namespace MWBase
|
|||
*/
|
||||
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 launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
|
||||
|
|
|
@ -124,10 +124,9 @@ namespace MWClass
|
|||
return info;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr) const
|
||||
std::shared_ptr<MWWorld::Action> Apparatus::use (const MWWorld::Ptr& ptr, bool force) 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
|
||||
|
|
|
@ -50,8 +50,7 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -354,9 +354,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -162,7 +162,7 @@ namespace MWClass
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
///< 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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -243,9 +243,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -519,6 +519,9 @@ namespace MWClass
|
|||
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
|
||||
return 0.f;
|
||||
|
||||
const GMST& gmst = getGmst();
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -36,8 +36,7 @@ namespace MWClass
|
|||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||
///< 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
|
||||
|
||||
static void registerSelf();
|
||||
|
|
|
@ -186,9 +186,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -55,8 +55,7 @@ namespace MWClass
|
|||
virtual std::string getInventoryIcon (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
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
|
||||
|
||||
virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const;
|
||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
|||
|
||||
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)
|
||||
const;
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -226,7 +226,7 @@ namespace MWClass
|
|||
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()))
|
||||
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::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
|
||||
|
||||
virtual float getWeight (const MWWorld::ConstPtr& ptr) const;
|
||||
|
|
|
@ -773,22 +773,24 @@ namespace MWClass
|
|||
float x = damage / (damage + getArmorRating(ptr));
|
||||
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
|
||||
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
|
||||
if (damage < 1)
|
||||
damage = 1;
|
||||
damage = std::max(1.f, damage);
|
||||
damageDiff = std::max(1, damageDiff);
|
||||
|
||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
||||
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
||||
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
|
||||
{
|
||||
int armorhealth = armor.getClass().getItemHealth(armor);
|
||||
armorhealth -= std::min(std::max(1, damageDiff),
|
||||
armorhealth);
|
||||
armor.getCellRef().setCharge(armorhealth);
|
||||
if (attacker.isEmpty() || (!attacker.isEmpty() && !(object.isEmpty() && !attacker.getClass().isNpc()))) // Unarmed creature attacks don't affect armor condition
|
||||
{
|
||||
int armorhealth = armor.getClass().getItemHealth(armor);
|
||||
armorhealth -= std::min(damageDiff, armorhealth);
|
||||
armor.getCellRef().setCharge(armorhealth);
|
||||
|
||||
// Armor broken? unequip it
|
||||
if (armorhealth == 0)
|
||||
armor = *inv.unequipItem(armor, ptr);
|
||||
// Armor broken? unequip it
|
||||
if (armorhealth == 0)
|
||||
armor = *inv.unequipItem(armor, ptr);
|
||||
}
|
||||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
|
||||
|
@ -923,6 +925,10 @@ namespace MWClass
|
|||
|
||||
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 GMST& gmst = getGmst();
|
||||
|
||||
|
@ -931,8 +937,8 @@ namespace MWClass
|
|||
|
||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||
|
||||
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
||||
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
|
||||
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
|
||||
|
@ -1073,7 +1079,7 @@ namespace MWClass
|
|||
{
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
|
||||
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
|
||||
|
|
|
@ -139,7 +139,7 @@ namespace MWClass
|
|||
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 =
|
||||
ptr.get<ESM::Potion>();
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace MWClass
|
|||
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
|
||||
///< 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
|
||||
|
||||
static void registerSelf();
|
||||
|
|
|
@ -140,9 +140,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -53,8 +53,7 @@ namespace MWClass
|
|||
|
||||
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)
|
||||
const;
|
||||
virtual std::shared_ptr<MWWorld::Action> use (const MWWorld::Ptr& ptr, bool force=false) const;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -149,9 +149,9 @@ namespace MWClass
|
|||
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
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
|||
|
||||
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;
|
||||
///< Generate action for using via inventory menu (default implementation: return a
|
||||
/// null action).
|
||||
|
|
|
@ -401,9 +401,9 @@ namespace MWClass
|
|||
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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
/// Second item in the pair specifies the error message
|
||||
|
||||
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
|
||||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
|
|
@ -629,6 +629,9 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::onTopicActivated(const std::string &topicId)
|
||||
{
|
||||
if (mGoodbye)
|
||||
return;
|
||||
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(topicId, mCallback.get());
|
||||
updateTopics();
|
||||
}
|
||||
|
|
|
@ -31,13 +31,16 @@ namespace MWGui
|
|||
|
||||
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);
|
||||
int index = lowerText.rfind("<br>");
|
||||
if (index == -1)
|
||||
int brIndex = lowerText.rfind("<br>");
|
||||
int pIndex = lowerText.rfind("<p>");
|
||||
if (brIndex == pIndex)
|
||||
mText = "";
|
||||
else if (brIndex > pIndex)
|
||||
mText = mText.substr(0, brIndex+4);
|
||||
else
|
||||
mText = mText.substr(0, index+4);
|
||||
mText = mText.substr(0, pIndex+3);
|
||||
|
||||
registerTag("br", Event_BrTag);
|
||||
registerTag("p", Event_PTag);
|
||||
|
|
|
@ -158,7 +158,9 @@ namespace MWGui
|
|||
|
||||
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->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
@ -474,7 +474,7 @@ namespace MWGui
|
|||
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);
|
||||
|
||||
|
@ -483,13 +483,24 @@ 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
|
||||
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
|
||||
{
|
||||
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||
if (canEquip.first == 0)
|
||||
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
std::pair<int, std::string> canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||
|
||||
if (canEquip.first == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item has a script, set its OnPcEquip to 1
|
||||
|
@ -512,9 +523,8 @@ namespace MWGui
|
|||
{
|
||||
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
|
||||
{
|
||||
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr);
|
||||
|
||||
action->execute (player);
|
||||
std::shared_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
||||
action->execute(player);
|
||||
}
|
||||
else
|
||||
mSkippedToEquip = ptr;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWGui
|
|||
|
||||
void clear();
|
||||
|
||||
void useItem(const MWWorld::Ptr& ptr);
|
||||
void useItem(const MWWorld::Ptr& ptr, bool force=false);
|
||||
|
||||
void setGuiMode(GuiMode mode);
|
||||
|
||||
|
|
|
@ -679,7 +679,9 @@ namespace MWGui
|
|||
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
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);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace MWGui
|
|||
|
||||
MyGUI::Widget* getDefaultKeyFocus() override;
|
||||
|
||||
virtual bool exit() { return false; }
|
||||
virtual bool exit() override { return false; }
|
||||
|
||||
bool mMarkedToDelete;
|
||||
|
||||
|
|
|
@ -33,44 +33,41 @@ namespace MWGui
|
|||
|
||||
QuickKeysMenu::QuickKeysMenu()
|
||||
: WindowBase("openmw_quickkeys_menu.layout")
|
||||
, mKey(std::vector<keyData>(10))
|
||||
, mSelected(nullptr)
|
||||
, mActivated(nullptr)
|
||||
, mAssignDialog(0)
|
||||
, mItemSelectionDialog(0)
|
||||
, mMagicSelectionDialog(0)
|
||||
, mSelectedIndex(-1)
|
||||
, mActivatedIndex(-1)
|
||||
|
||||
{
|
||||
getWidget(mOkButton, "OKButton");
|
||||
getWidget(mInstructionLabel, "InstructionLabel");
|
||||
|
||||
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);
|
||||
center();
|
||||
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
ItemWidget* button;
|
||||
getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1));
|
||||
mKey[i].index = 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);
|
||||
|
||||
mQuickKeyButtons.push_back(button);
|
||||
|
||||
mAssigned.push_back(Type_Unassigned);
|
||||
|
||||
unassign(button, i);
|
||||
unassign(&mKey[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void QuickKeysMenu::clear()
|
||||
{
|
||||
mActivatedIndex = -1;
|
||||
mActivated = nullptr;
|
||||
|
||||
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
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
ItemWidget* button = mQuickKeyButtons[i];
|
||||
int type = mAssigned[i];
|
||||
|
||||
switch (type)
|
||||
switch (mKey[i].type)
|
||||
{
|
||||
case Type_Unassigned:
|
||||
case Type_HandToHand:
|
||||
|
@ -103,49 +97,55 @@ namespace MWGui
|
|||
case Type_Item:
|
||||
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
|
||||
if (item.getRefData().getCount() < 1 ||
|
||||
if (!item || 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(mKey[i].id);
|
||||
|
||||
if (item)
|
||||
mKey[i].button->setUserData(MWWorld::Ptr(item));
|
||||
|
||||
item = store.findReplacement(id);
|
||||
button->setUserData(MWWorld::Ptr(item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void QuickKeysMenu::unassign(ItemWidget* key, int index)
|
||||
void QuickKeysMenu::unassign(keyData* key)
|
||||
{
|
||||
key->clearUserStrings();
|
||||
key->setItem(MWWorld::Ptr());
|
||||
while (key->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0));
|
||||
key->button->clearUserStrings();
|
||||
key->button->setItem(MWWorld::Ptr());
|
||||
|
||||
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);
|
||||
|
||||
image->setImageTexture("icons\\k\\stealth_handtohand.dds");
|
||||
image->setNeedMouseFocus(false);
|
||||
}
|
||||
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);
|
||||
textBox->setTextAlign (MyGUI::Align::Center);
|
||||
textBox->setCaption (MyGUI::utility::toString(index+1));
|
||||
textBox->setNeedMouseFocus (false);
|
||||
MyGUI::TextBox* textBox = key->button->createWidgetReal<MyGUI::TextBox>("SandText",
|
||||
MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
|
||||
|
||||
textBox->setTextAlign(MyGUI::Align::Center);
|
||||
textBox->setCaption(MyGUI::utility::toString(key->index));
|
||||
textBox->setNeedMouseFocus(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,19 +154,24 @@ namespace MWGui
|
|||
int index = -1;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(index != -1);
|
||||
mSelectedIndex = index;
|
||||
mSelected = &mKey[index];
|
||||
|
||||
// prevent reallocation of zero key from Type_HandToHand
|
||||
if(mSelected->index == 10)
|
||||
return;
|
||||
|
||||
// open assign dialog
|
||||
if (!mAssignDialog)
|
||||
mAssignDialog = new QuickKeysMenuAssign(this);
|
||||
mAssignDialog->setVisible (true);
|
||||
|
||||
mAssignDialog->setVisible(true);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)
|
||||
|
@ -174,10 +179,9 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);
|
||||
}
|
||||
|
||||
|
||||
void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)
|
||||
{
|
||||
if (!mItemSelectionDialog )
|
||||
if (!mItemSelectionDialog)
|
||||
{
|
||||
mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}");
|
||||
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);
|
||||
|
@ -187,43 +191,45 @@ namespace MWGui
|
|||
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
|
||||
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
|
||||
|
||||
mAssignDialog->setVisible (false);
|
||||
mAssignDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)
|
||||
{
|
||||
if (!mMagicSelectionDialog )
|
||||
if (!mMagicSelectionDialog)
|
||||
{
|
||||
mMagicSelectionDialog = new MagicSelectionDialog(this);
|
||||
}
|
||||
mMagicSelectionDialog->setVisible(true);
|
||||
|
||||
mAssignDialog->setVisible (false);
|
||||
mAssignDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)
|
||||
{
|
||||
unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex);
|
||||
mAssignDialog->setVisible (false);
|
||||
unassign(mSelected);
|
||||
mAssignDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)
|
||||
{
|
||||
mAssignDialog->setVisible (false);
|
||||
mAssignDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
|
||||
{
|
||||
assert (mSelectedIndex >= 0);
|
||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
||||
while (button->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
||||
assert(mSelected);
|
||||
|
||||
mAssigned[mSelectedIndex] = Type_Item;
|
||||
while (mSelected->button->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
|
||||
|
||||
button->setItem(item, ItemWidget::Barter);
|
||||
button->setUserString ("ToolTipType", "ItemPtr");
|
||||
button->setUserData(MWWorld::Ptr(item));
|
||||
mSelected->type = Type_Item;
|
||||
mSelected->id = item.getCellRef().getRefId();
|
||||
mSelected->name = item.getClass().getName(item);
|
||||
|
||||
mSelected->button->setItem(item, ItemWidget::Barter);
|
||||
mSelected->button->setUserString("ToolTipType", "ItemPtr");
|
||||
mSelected->button->setUserData(item);
|
||||
|
||||
if (mItemSelectionDialog)
|
||||
mItemSelectionDialog->setVisible(false);
|
||||
|
@ -234,40 +240,39 @@ namespace MWGui
|
|||
mItemSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
|
||||
void QuickKeysMenu::onAssignMagicItem(MWWorld::Ptr item)
|
||||
{
|
||||
assert (mSelectedIndex >= 0);
|
||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
||||
while (button->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
||||
assert(mSelected);
|
||||
|
||||
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));
|
||||
button->setIcon(item);
|
||||
mSelected->type = Type_MagicItem;
|
||||
|
||||
button->setUserString ("ToolTipType", "ItemPtr");
|
||||
button->setUserData(MWWorld::Ptr(item));
|
||||
mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||
mSelected->button->setIcon(item);
|
||||
|
||||
mSelected->button->setUserString("ToolTipType", "ItemPtr");
|
||||
mSelected->button->setUserData(MWWorld::Ptr(item));
|
||||
|
||||
if (mMagicSelectionDialog)
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
|
||||
void QuickKeysMenu::onAssignMagic(const std::string& spellId)
|
||||
{
|
||||
assert (mSelectedIndex >= 0);
|
||||
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
|
||||
while (button->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
|
||||
assert(mSelected);
|
||||
while (mSelected->button->getChildCount()) // Destroy number label
|
||||
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
|
||||
|
||||
mAssigned[mSelectedIndex] = Type_Magic;
|
||||
mSelected->type = Type_Magic;
|
||||
mSelected->id = spellId;
|
||||
|
||||
button->setItem(MWWorld::Ptr());
|
||||
button->setUserString ("ToolTipType", "Spell");
|
||||
button->setUserString ("Spell", spellId);
|
||||
mSelected->button->setItem(MWWorld::Ptr());
|
||||
mSelected->button->setUserString("ToolTipType", "Spell");
|
||||
mSelected->button->setUserString("Spell", spellId);
|
||||
|
||||
const MWWorld::ESMStore &esmStore =
|
||||
MWBase::Environment::get().getWorld()->getStore();
|
||||
const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
// use the icon of the first effect
|
||||
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);
|
||||
|
@ -280,14 +285,14 @@ namespace MWGui
|
|||
path.insert(slashPos+1, "b_");
|
||||
path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);
|
||||
|
||||
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||
button->setIcon(path);
|
||||
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
|
||||
mSelected->button->setIcon(path);
|
||||
|
||||
if (mMagicSelectionDialog)
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignMagicCancel ()
|
||||
void QuickKeysMenu::onAssignMagicCancel()
|
||||
{
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
@ -295,18 +300,17 @@ namespace MWGui
|
|||
void QuickKeysMenu::updateActivatedQuickKey()
|
||||
{
|
||||
// there is no delayed action, nothing to do.
|
||||
if (mActivatedIndex < 0)
|
||||
if (!mActivated)
|
||||
return;
|
||||
|
||||
activateQuickKey(mActivatedIndex);
|
||||
activateQuickKey(mActivated->index);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::activateQuickKey(int index)
|
||||
{
|
||||
assert (index-1 >= 0);
|
||||
ItemWidget* button = mQuickKeyButtons[index-1];
|
||||
assert(index >= 1 && index <= 10);
|
||||
|
||||
QuickKeyType type = mAssigned[index-1];
|
||||
keyData *key = &mKey[index-1];
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
|
@ -319,117 +323,112 @@ namespace MWGui
|
|||
|| playerStats.getHitRecovery();
|
||||
|
||||
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
||||
if (isReturnNeeded && type != Type_Item)
|
||||
|
||||
if (isReturnNeeded && key->type != Type_Item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDelayNeeded && type != Type_Item)
|
||||
else if (isDelayNeeded && key->type != Type_Item)
|
||||
{
|
||||
mActivatedIndex = index;
|
||||
mActivated = key;
|
||||
return;
|
||||
}
|
||||
else
|
||||
mActivatedIndex = -1;
|
||||
|
||||
if (type == Type_Item || type == Type_MagicItem)
|
||||
{
|
||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
||||
// 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);
|
||||
button->setUserData(MWWorld::Ptr(item));
|
||||
|
||||
if (item.getRefData().getCount() < 1)
|
||||
{
|
||||
// No replacement was found
|
||||
MWBase::Environment::get().getWindowManager ()->messageBox (
|
||||
"#{sQuickMenu5} " + item.getClass().getName(item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
mActivated = nullptr;
|
||||
}
|
||||
|
||||
if (type == Type_Magic)
|
||||
if (key->type == Type_Item || key->type == Type_MagicItem)
|
||||
{
|
||||
std::string spellId = button->getUserString("Spell");
|
||||
MWWorld::Ptr item = *key->button->getUserData<MWWorld::Ptr>();
|
||||
|
||||
// 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 isTool = item.getTypeName() == typeid(ESM::Probe).name() || item.getTypeName() == typeid(ESM::Lockpick).name();
|
||||
|
||||
// delay weapon switching if player is busy
|
||||
if (isDelayNeeded && (isWeapon || isTool))
|
||||
{
|
||||
mActivatedIndex = index;
|
||||
return;
|
||||
}
|
||||
|
||||
// disable weapon switching if player is dead or paralyzed
|
||||
if (isReturnNeeded && (isWeapon || isTool))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
// change draw state only if the item is in player's right hand
|
||||
if (rightHand != store.end() && item == *rightHand)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
||||
}
|
||||
}
|
||||
else if (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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
assert(it != store.end());
|
||||
|
||||
// equip, if it can be equipped
|
||||
if (!item.getClass().getEquipmentSlots(item).first.empty())
|
||||
if (key->type == Type_Item)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
|
||||
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() ||
|
||||
item.getTypeName() == typeid(ESM::Lockpick).name();
|
||||
|
||||
// make sure that item was successfully equipped
|
||||
if (!store.isEquipped(item))
|
||||
// delay weapon switching if player is busy
|
||||
if (isDelayNeeded && (isWeapon || isTool))
|
||||
{
|
||||
mActivated = key;
|
||||
return;
|
||||
}
|
||||
else if (isReturnNeeded && (isWeapon || isTool))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
// change draw state only if the item is in player's right hand
|
||||
if (rightHand != store.end() && item == *rightHand)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
||||
}
|
||||
}
|
||||
else if (key->type == Type_MagicItem)
|
||||
{
|
||||
// equip, if it can be equipped
|
||||
if (!item.getClass().getEquipmentSlots(item).first.empty())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
|
||||
// make sure that item was successfully equipped
|
||||
if (!store.isEquipped(item))
|
||||
return;
|
||||
}
|
||||
|
||||
store.setSelectedEnchantItem(it);
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
|
||||
}
|
||||
}
|
||||
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(it);
|
||||
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_HandToHand)
|
||||
else if (key->type == Type_HandToHand)
|
||||
{
|
||||
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
|
||||
|
@ -491,9 +490,9 @@ namespace MWGui
|
|||
|
||||
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;
|
||||
key.mType = type;
|
||||
|
@ -541,30 +540,29 @@ namespace MWGui
|
|||
if (i >= 10)
|
||||
return;
|
||||
|
||||
mSelectedIndex = i;
|
||||
int keyType = it->mType;
|
||||
std::string id = it->mId;
|
||||
ItemWidget* button = mQuickKeyButtons[i];
|
||||
mSelected = &mKey[i];
|
||||
mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType;
|
||||
mSelected->id = it->mId;
|
||||
|
||||
switch (keyType)
|
||||
switch (mSelected->type)
|
||||
{
|
||||
case Type_Magic:
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id))
|
||||
onAssignMagic(id);
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(mSelected->id))
|
||||
onAssignMagic(mSelected->id);
|
||||
break;
|
||||
case Type_Item:
|
||||
case Type_MagicItem:
|
||||
{
|
||||
// Find the item by id
|
||||
MWWorld::Ptr item = store.findReplacement(id);
|
||||
MWWorld::Ptr item = store.findReplacement(mSelected->id);
|
||||
|
||||
if (item.isEmpty())
|
||||
unassign(button, i);
|
||||
unassign(&mKey[i]);
|
||||
else
|
||||
{
|
||||
if (keyType == Type_Item)
|
||||
if (mSelected->type == Type_Item)
|
||||
onAssignItem(item);
|
||||
else if (keyType == Type_MagicItem)
|
||||
else if (mSelected->type == Type_MagicItem)
|
||||
onAssignMagicItem(item);
|
||||
}
|
||||
|
||||
|
@ -572,7 +570,7 @@ namespace MWGui
|
|||
}
|
||||
case Type_Unassigned:
|
||||
case Type_HandToHand:
|
||||
unassign(button, i);
|
||||
unassign(&mKey[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,23 +55,31 @@ namespace MWGui
|
|||
|
||||
|
||||
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::Button* mOkButton;
|
||||
|
||||
std::vector<ItemWidget*> mQuickKeyButtons;
|
||||
std::vector<QuickKeyType> mAssigned;
|
||||
|
||||
QuickKeysMenuAssign* mAssignDialog;
|
||||
ItemSelectionDialog* mItemSelectionDialog;
|
||||
MagicSelectionDialog* mMagicSelectionDialog;
|
||||
|
||||
int mSelectedIndex;
|
||||
int mActivatedIndex;
|
||||
|
||||
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
|
||||
void onOkButtonClicked(MyGUI::Widget* sender);
|
||||
|
||||
void unassign(ItemWidget* key, int index);
|
||||
void unassign(keyData* key);
|
||||
};
|
||||
|
||||
class QuickKeysMenuAssign : public WindowModal
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace MWGui
|
|||
|
||||
newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId);
|
||||
newSpell.mActive = true;
|
||||
newSpell.mCount = 1;
|
||||
mSpells.push_back(newSpell);
|
||||
}
|
||||
|
||||
|
@ -109,6 +110,7 @@ namespace MWGui
|
|||
newSpell.mItem = item;
|
||||
newSpell.mId = item.getCellRef().getRefId();
|
||||
newSpell.mName = item.getClass().getName(item);
|
||||
newSpell.mCount = item.getRefData().getCount();
|
||||
newSpell.mType = Spell::Type_EnchantedItem;
|
||||
newSpell.mSelected = invStore.getSelectedEnchantItem() == it;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace MWGui
|
|||
std::string mCostColumn; // Cost/chance or Cost/charge
|
||||
std::string mId; // Item ID or spell ID
|
||||
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 mActive; // (Items only) is the item equipped?
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <components/widgets/sharedstatebutton.hpp>
|
||||
|
||||
#include "tooltips.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
@ -103,11 +105,12 @@ namespace MWGui
|
|||
}
|
||||
|
||||
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,
|
||||
MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
t->setNeedKeyFocus(true);
|
||||
t->setCaption(spell.mName);
|
||||
t->setCaption(spell.mName + captionSuffix);
|
||||
t->setTextAlign(MyGUI::Align::Left);
|
||||
adjustSpellWidget(spell, i, t);
|
||||
|
||||
|
@ -163,7 +166,8 @@ namespace MWGui
|
|||
|
||||
// more checking for major change.
|
||||
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;
|
||||
break;
|
||||
|
|
|
@ -191,6 +191,9 @@ namespace MWGui
|
|||
else if (type == "ItemPtr")
|
||||
{
|
||||
mFocusObject = *focus->getUserData<MWWorld::Ptr>();
|
||||
if (!mFocusObject)
|
||||
return;
|
||||
|
||||
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
|
||||
}
|
||||
else if (type == "ItemModelIndex")
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace MWGui
|
|||
|
||||
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));
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ namespace MWGui
|
|||
return;
|
||||
|
||||
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}");
|
||||
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
|
||||
{
|
||||
public:
|
||||
WindowModal(const std::string& parLayout);
|
||||
virtual void onOpen();
|
||||
virtual void onClose();
|
||||
virtual bool exit() {return true;}
|
||||
virtual void onOpen() override;
|
||||
virtual void onClose() override;
|
||||
virtual bool exit() override {return true;}
|
||||
};
|
||||
|
||||
/// 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::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; }
|
||||
|
||||
void WindowManager::useItem(const MWWorld::Ptr &item)
|
||||
void WindowManager::useItem(const MWWorld::Ptr &item, bool bypassBeastRestrictions)
|
||||
{
|
||||
if (mInventoryWindow)
|
||||
mInventoryWindow->useItem(item);
|
||||
mInventoryWindow->useItem(item, bypassBeastRestrictions);
|
||||
}
|
||||
|
||||
bool WindowManager::isAllowed (GuiWindow wnd) const
|
||||
|
|
|
@ -185,7 +185,7 @@ namespace MWGui
|
|||
virtual MWGui::TradeWindow* getTradeWindow();
|
||||
|
||||
/// 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();
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
|
||||
{
|
||||
mCharacterController.reset(new CharacterController(ptr, animation));
|
||||
|
@ -19,10 +18,4 @@ namespace MWMechanics
|
|||
{
|
||||
return mCharacterController.get();
|
||||
}
|
||||
|
||||
AiState& Actor::getAiState()
|
||||
{
|
||||
return mAiState;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "aistate.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
|
@ -29,12 +27,8 @@ namespace MWMechanics
|
|||
|
||||
CharacterController* getCharacterController();
|
||||
|
||||
AiState& getAiState();
|
||||
|
||||
private:
|
||||
std::unique_ptr<CharacterController> mCharacterController;
|
||||
|
||||
AiState mAiState;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -766,6 +766,8 @@ namespace MWMechanics
|
|||
|
||||
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
bool actorKilled = false;
|
||||
|
||||
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
||||
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
||||
|
@ -793,10 +795,14 @@ namespace MWMechanics
|
|||
caster.getClass().getNpcStats(caster).addWerewolfKill();
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
|
||||
actorKilled = true;
|
||||
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)
|
||||
{
|
||||
if (!actor.getClass().isActor())
|
||||
|
@ -1365,7 +1378,7 @@ namespace MWMechanics
|
|||
{
|
||||
CreatureStats &stats = iter->first.getClass().getCreatureStats(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();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
PtrActorMap::const_iterator it = mActors.find(ptr);
|
||||
|
@ -1993,7 +2015,7 @@ namespace MWMechanics
|
|||
|| ptr.getClass().getCreatureStats(ptr).isParalyzed())
|
||||
continue;
|
||||
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.
|
||||
|
||||
void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
|
||||
|
||||
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
|
||||
///< Updates an actor with a new Ptr
|
||||
|
||||
|
@ -161,6 +163,7 @@ namespace MWMechanics
|
|||
|
||||
void clear(); // Clear death counter
|
||||
|
||||
bool isCastingSpell(const MWWorld::Ptr& ptr) const;
|
||||
bool isReadyToBlock(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
|
||||
{
|
||||
|
||||
/// \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)
|
||||
{
|
||||
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
|
@ -115,7 +47,7 @@ namespace MWMechanics
|
|||
|
||||
void AiCombat::init()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,7 +23,72 @@ namespace MWMechanics
|
|||
{
|
||||
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
|
||||
class AiCombat : public AiPackage
|
||||
|
|
|
@ -17,22 +17,6 @@
|
|||
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;
|
||||
|
||||
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
|
||||
|
|
|
@ -19,6 +19,21 @@ namespace AiSequence
|
|||
|
||||
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
|
||||
/** 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,
|
||||
TypeIdFace = 8,
|
||||
TypeIdBreathe = 9,
|
||||
TypeIdInternalTravel = 10
|
||||
TypeIdInternalTravel = 10,
|
||||
TypeIdCast = 11
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
|
|
|
@ -180,15 +180,11 @@ bool AiSequence::isPackageDone() const
|
|||
|
||||
bool isActualAiPackage(int packageTypeId)
|
||||
{
|
||||
return (packageTypeId != AiPackage::TypeIdCombat
|
||||
&& packageTypeId != AiPackage::TypeIdPursue
|
||||
&& packageTypeId != AiPackage::TypeIdAvoidDoor
|
||||
&& packageTypeId != AiPackage::TypeIdFace
|
||||
&& packageTypeId != AiPackage::TypeIdBreathe
|
||||
&& packageTypeId != AiPackage::TypeIdInternalTravel);
|
||||
return (packageTypeId >= AiPackage::TypeIdWander &&
|
||||
packageTypeId <= AiPackage::TypeIdActivate);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
|
@ -262,7 +258,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
|
||||
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
|
||||
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()))
|
||||
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.
|
||||
// Also there is no point to stack return packages.
|
||||
int currentTypeId = getTypeId();
|
||||
|
@ -316,7 +312,8 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
|||
if (currentTypeId <= MWMechanics::AiPackage::TypeIdWander
|
||||
&& !hasPackage(MWMechanics::AiPackage::TypeIdInternalTravel)
|
||||
&& (newTypeId <= MWMechanics::AiPackage::TypeIdCombat
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue))
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdPursue
|
||||
|| newTypeId == MWMechanics::AiPackage::TypeIdCast))
|
||||
{
|
||||
osg::Vec3f dest;
|
||||
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
|
||||
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())
|
||||
{
|
||||
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());
|
||||
|
||||
// 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()
|
||||
|
@ -494,12 +506,12 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
mLastAiPackage = sequence.mLastAiPackage;
|
||||
}
|
||||
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
||||
void AiSequence::fastForward(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (!mPackages.empty())
|
||||
{
|
||||
MWMechanics::AiPackage* package = mPackages.front();
|
||||
package->fastForward(actor, state);
|
||||
package->fastForward(actor, mAiState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
#include "aistate.hpp"
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -47,6 +49,7 @@ namespace MWMechanics
|
|||
|
||||
/// The type of AI package that ran last
|
||||
int mLastAiPackage;
|
||||
AiState mAiState;
|
||||
|
||||
public:
|
||||
///Default constructor
|
||||
|
@ -104,10 +107,10 @@ namespace MWMechanics
|
|||
void stopPursuit();
|
||||
|
||||
/// 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
|
||||
void fastForward(const MWWorld::Ptr &actor, AiState &state);
|
||||
void fastForward(const MWWorld::Ptr &actor);
|
||||
|
||||
/// Remove all packages.
|
||||
void clear();
|
||||
|
|
|
@ -53,7 +53,23 @@ namespace MWMechanics
|
|||
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), pos.asVec3()))
|
||||
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;
|
||||
return true;
|
||||
|
|
|
@ -51,67 +51,6 @@ namespace MWMechanics
|
|||
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):
|
||||
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))
|
||||
|
@ -221,7 +160,7 @@ namespace MWMechanics
|
|||
mPathFinder.buildSyncedPath(start, dest, actor.getCell(), getPathGridGraph(actor.getCell()));
|
||||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(Wander_Walking);
|
||||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
}
|
||||
|
||||
doPerFrameActionsForState(actor, duration, storage, pos);
|
||||
|
@ -270,7 +209,7 @@ namespace MWMechanics
|
|||
|
||||
if(actorCanMoveByZ && mDistance > 0) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -283,7 +222,7 @@ namespace MWMechanics
|
|||
if (Misc::Rng::rollDice(100) >= 96) {
|
||||
wanderNearStart(actor, storage, mDistance);
|
||||
} else {
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
} else if (storage.mAllowedNodes.empty() && !storage.mIsWanderingManually) {
|
||||
storage.mCanWanderAlongPathGrid = false;
|
||||
|
@ -299,13 +238,13 @@ namespace MWMechanics
|
|||
mDistance = 0;
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == Wander_IdleNow) || (wanderState == Wander_Walking))
|
||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
||||
{
|
||||
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
|
||||
if(!mPathFinder.isPathConstructed())
|
||||
|
@ -381,7 +320,7 @@ namespace MWMechanics
|
|||
|
||||
if (mPathFinder.isPathConstructed())
|
||||
{
|
||||
storage.setState(Wander_Walking, true);
|
||||
storage.setState(AiWanderStorage::Wander_Walking, true);
|
||||
mHasDestination = true;
|
||||
}
|
||||
return;
|
||||
|
@ -410,26 +349,26 @@ namespace MWMechanics
|
|||
void AiWander::completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage) {
|
||||
stopWalking(actor, storage);
|
||||
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)
|
||||
{
|
||||
switch (storage.mState)
|
||||
{
|
||||
case Wander_IdleNow:
|
||||
case AiWanderStorage::Wander_IdleNow:
|
||||
onIdleStatePerFrameActions(actor, duration, storage);
|
||||
break;
|
||||
|
||||
case Wander_Walking:
|
||||
case AiWanderStorage::Wander_Walking:
|
||||
onWalkingStatePerFrameActions(actor, duration, storage, pos);
|
||||
break;
|
||||
|
||||
case Wander_ChooseAction:
|
||||
case AiWanderStorage::Wander_ChooseAction:
|
||||
onChooseActionStatePerFrameActions(actor, storage);
|
||||
break;
|
||||
|
||||
case Wander_MoveNow:
|
||||
case AiWanderStorage::Wander_MoveNow:
|
||||
break; // nothing to do
|
||||
|
||||
default:
|
||||
|
@ -451,7 +390,7 @@ namespace MWMechanics
|
|||
if (mDistance && // actor is not intended to be stationary
|
||||
proximityToDoor(actor, distance*1.6f))
|
||||
{
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
storage.mTrimCurrentNode = false; // just in case
|
||||
return;
|
||||
}
|
||||
|
@ -468,13 +407,13 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
||||
{
|
||||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(Wander_Walking);
|
||||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
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))
|
||||
{
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -502,7 +441,7 @@ namespace MWMechanics
|
|||
|
||||
if (!idleAnimation && mDistance)
|
||||
{
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
return;
|
||||
}
|
||||
if(idleAnimation)
|
||||
|
@ -512,13 +451,13 @@ namespace MWMechanics
|
|||
if(!playIdle(actor, idleAnimation))
|
||||
{
|
||||
storage.mBadIdles.push_back(idleAnimation);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
||||
|
@ -534,7 +473,7 @@ namespace MWMechanics
|
|||
trimAllowedNodes(storage.mAllowedNodes, mPathFinder);
|
||||
mObstacleCheck.clear();
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.setState(AiWanderStorage::Wander_MoveNow);
|
||||
}
|
||||
|
||||
storage.mStuckCount++; // TODO: maybe no longer needed
|
||||
|
@ -545,7 +484,7 @@ namespace MWMechanics
|
|||
{
|
||||
mObstacleCheck.clear();
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
storage.setState(AiWanderStorage::Wander_ChooseAction);
|
||||
storage.mStuckCount = 0;
|
||||
}
|
||||
}
|
||||
|
@ -596,8 +535,8 @@ namespace MWMechanics
|
|||
float playerDistSqr = (playerPos - actorPos).length2();
|
||||
|
||||
int& greetingTimer = storage.mGreetingTimer;
|
||||
GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (greetingState == Greet_None)
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (greetingState == AiWanderStorage::Greet_None)
|
||||
{
|
||||
if ((playerDistSqr <= helloDistance*helloDistance) &&
|
||||
!player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||
|
@ -606,37 +545,37 @@ namespace MWMechanics
|
|||
|
||||
if (greetingTimer >= GREETING_SHOULD_START)
|
||||
{
|
||||
greetingState = Greet_InProgress;
|
||||
greetingState = AiWanderStorage::Greet_InProgress;
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == Greet_InProgress)
|
||||
if (greetingState == AiWanderStorage::Greet_InProgress)
|
||||
{
|
||||
greetingTimer++;
|
||||
|
||||
if (storage.mState == Wander_Walking)
|
||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||
{
|
||||
stopWalking(actor, storage, false);
|
||||
mObstacleCheck.clear();
|
||||
storage.setState(Wander_IdleNow);
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_END)
|
||||
{
|
||||
greetingState = Greet_Done;
|
||||
greetingState = AiWanderStorage::Greet_Done;
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == MWMechanics::AiWander::Greet_Done)
|
||||
if (greetingState == AiWanderStorage::Greet_Done)
|
||||
{
|
||||
float resetDist = 2 * helloDistance;
|
||||
if (playerDistSqr >= resetDist*resetDist)
|
||||
greetingState = Greet_None;
|
||||
greetingState = AiWanderStorage::Greet_None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,7 +615,7 @@ namespace MWMechanics
|
|||
storage.mAllowedNodes.push_back(storage.mCurrentNode);
|
||||
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:
|
||||
else
|
||||
|
|
|
@ -21,8 +21,81 @@ namespace ESM
|
|||
}
|
||||
|
||||
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
|
||||
class AiWander : public AiPackage
|
||||
|
@ -52,19 +125,6 @@ namespace MWMechanics
|
|||
|
||||
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:
|
||||
// NOTE: mDistance and mDuration must be set already
|
||||
void init();
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "aicombataction.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
@ -370,6 +371,10 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
|
|||
{
|
||||
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
||||
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;
|
||||
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
||||
{
|
||||
movementAnimName += weap->shortgroup;
|
||||
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;
|
||||
if(!mAnimation->hasAnimation(movementAnimName))
|
||||
{
|
||||
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||
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
|
||||
* beginning. */
|
||||
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1);
|
||||
// If we're playing the same animation, start it from the point it ended
|
||||
bool sameAnim = (movementAnimName == mCurrentMovement);
|
||||
float startPoint = 0.f;
|
||||
if (sameAnim)
|
||||
mAnimation->getInfo(mCurrentMovement, &startPoint);
|
||||
|
||||
mMovementAnimationControlled = true;
|
||||
|
||||
|
@ -510,7 +524,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Make sure the character was swimming upon death for forward-compatibility
|
||||
const bool wasSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
|
||||
|
||||
switch (death)
|
||||
{
|
||||
case CharState_SwimDeath:
|
||||
mCurrentDeath = "swimdeath";
|
||||
break;
|
||||
case CharState_SwimDeathKnockDown:
|
||||
mCurrentDeath = "swimdeathknockdown";
|
||||
mCurrentDeath = (wasSwimming ? "swimdeathknockdown" : "deathknockdown");
|
||||
break;
|
||||
case CharState_SwimDeathKnockOut:
|
||||
mCurrentDeath = "swimdeathknockout";
|
||||
mCurrentDeath = (wasSwimming ? "swimdeathknockout" : "deathknockout");
|
||||
break;
|
||||
case CharState_DeathKnockDown:
|
||||
mCurrentDeath = "deathknockdown";
|
||||
|
@ -778,6 +795,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
, mSecondsOfRunning(0)
|
||||
, mTurnAnimationThreshold(0)
|
||||
, mAttackingOrSpell(false)
|
||||
, mCastingManualSpell(false)
|
||||
, mTimeUntilWake(0.f)
|
||||
{
|
||||
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.
|
||||
&& 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)
|
||||
|
@ -1072,13 +1091,18 @@ bool CharacterController::updateCreatureState()
|
|||
if (weapType == WeapType_Spell)
|
||||
{
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellid);
|
||||
|
@ -1205,12 +1229,17 @@ bool CharacterController::updateWeaponState()
|
|||
&& mUpperBodyState != UpperCharState_UnEquipingWeap
|
||||
&& !isStillWeapon)
|
||||
{
|
||||
// Note: we do not disable unequipping animation automatically to avoid body desync
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
// 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
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
}
|
||||
|
||||
if(!downSoundId.empty())
|
||||
{
|
||||
|
@ -1351,10 +1380,11 @@ bool CharacterController::updateWeaponState()
|
|||
stats.getSpells().setSelectedSpell(selectedSpell);
|
||||
}
|
||||
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);
|
||||
|
||||
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
||||
|
@ -1622,16 +1652,18 @@ bool CharacterController::updateWeaponState()
|
|||
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())
|
||||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
MWRender::Animation::BlendMask_UpperBody, true,
|
||||
weapSpeed, start, stop, 0.0f, 0);
|
||||
else
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
MWRender::Animation::BlendMask_UpperBody, false,
|
||||
weapSpeed, start, stop, 0.0f, 0);
|
||||
}
|
||||
}
|
||||
|
@ -2141,7 +2173,7 @@ void CharacterController::unpersistAnimationState()
|
|||
|
||||
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -2359,6 +2391,11 @@ bool CharacterController::isAttackPrepairing() const
|
|||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
|
||||
}
|
||||
|
||||
bool CharacterController::isCastingSpell() const
|
||||
{
|
||||
return mCastingManualSpell || mUpperBodyState == UpperCharState_CastingSpell;
|
||||
}
|
||||
|
||||
bool CharacterController::isReadyToBlock() const
|
||||
{
|
||||
return updateCarriedLeftVisible(mWeaponType);
|
||||
|
@ -2422,6 +2459,14 @@ void CharacterController::setAttackingOrSpell(bool 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)
|
||||
{
|
||||
mAttackType = attackType;
|
||||
|
|
|
@ -204,6 +204,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
std::string mAttackType; // slash, chop or thrust
|
||||
|
||||
bool mAttackingOrSpell;
|
||||
bool mCastingManualSpell;
|
||||
|
||||
float mTimeUntilWake;
|
||||
|
||||
|
@ -276,6 +277,7 @@ public:
|
|||
void forceStateUpdate();
|
||||
|
||||
bool isAttackPrepairing() const;
|
||||
bool isCastingSpell() const;
|
||||
bool isReadyToBlock() const;
|
||||
bool isKnockedDown() const;
|
||||
bool isKnockedOut() const;
|
||||
|
@ -286,6 +288,7 @@ public:
|
|||
bool isAttackingOrSpell() const;
|
||||
|
||||
void setAttackingOrSpell(bool attackingOrSpell);
|
||||
void castSpell(const std::string spellId, bool manualSpell=false);
|
||||
void setAIAttackType(const std::string& attackType);
|
||||
static void setAttackTypeRandomly(std::string& attackType);
|
||||
|
||||
|
|
|
@ -115,14 +115,16 @@ namespace MWMechanics
|
|||
|
||||
if (Misc::Rng::roll0to99() < x)
|
||||
{
|
||||
// Reduce shield durability by incoming damage
|
||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||
|
||||
shieldhealth -= std::min(shieldhealth, int(damage));
|
||||
shield->getCellRef().setCharge(shieldhealth);
|
||||
if (shieldhealth == 0)
|
||||
inv.unequipItem(*shield, blocker);
|
||||
if (!(weapon.isEmpty() && !attacker.getClass().isNpc())) // Unarmed creature attacks don't affect armor condition
|
||||
{
|
||||
// Reduce shield durability by incoming damage
|
||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||
|
||||
shieldhealth -= std::min(shieldhealth, int(damage));
|
||||
shield->getCellRef().setCharge(shieldhealth);
|
||||
if (shieldhealth == 0)
|
||||
inv.unequipItem(*shield, blocker);
|
||||
}
|
||||
// Reduce blocker fatigue
|
||||
const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat();
|
||||
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue