mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:09:40 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor
This commit is contained in:
commit
d73c1c8590
349 changed files with 12858 additions and 2571 deletions
|
@ -172,6 +172,20 @@ Clang_Format:
|
|||
- CI/check_file_names.sh
|
||||
- CI/check_clang_format.sh
|
||||
|
||||
Lupdate:
|
||||
extends: .Ubuntu_Image
|
||||
stage: checks
|
||||
cache:
|
||||
key: Ubuntu_lupdate.ubuntu_22.04.v1
|
||||
paths:
|
||||
- apt-cache/
|
||||
variables:
|
||||
LUPDATE: lupdate
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh openmw-qt-translations
|
||||
script:
|
||||
- CI/check_qt_translations.sh
|
||||
|
||||
Teal:
|
||||
stage: checks
|
||||
extends: .Ubuntu_Image
|
||||
|
@ -196,6 +210,7 @@ Ubuntu_GCC_Debug:
|
|||
CCACHE_SIZE: 3G
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: -O0
|
||||
BUILD_SHARED_LIBS: 1
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ Programmers
|
|||
Eduard Cot (trombonecot)
|
||||
Eli2
|
||||
Emanuel Guével (potatoesmaster)
|
||||
Epoch
|
||||
Eris Caffee (eris)
|
||||
eroen
|
||||
escondida
|
||||
|
@ -187,6 +188,7 @@ Programmers
|
|||
pkubik
|
||||
PLkolek
|
||||
PlutonicOverkill
|
||||
Qlonever
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
Rafael Moura (dhustkoder)
|
||||
Randy Davin (Kindi)
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -24,7 +24,9 @@
|
|||
Bug #5129: Stuttering animation on Centurion Archer
|
||||
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
||||
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
||||
Bug #5413: Enemies do a battlecry everytime the player summons a creature
|
||||
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
|
||||
Bug #5755: Reset friendly hit counter
|
||||
Bug #5849: Paralysis breaks landing
|
||||
Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla
|
||||
Bug #5883: Immobile creatures don't cause water ripples
|
||||
|
@ -37,6 +39,7 @@
|
|||
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
|
||||
Bug #6427: Enemy health bar disappears before damaging effect ends
|
||||
Bug #6550: Cloned body parts don't inherit texture effects
|
||||
Bug #6574: Crash at far away from world origin coordinates
|
||||
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
||||
Bug #6657: Distant terrain tiles become black when using FWIW mod
|
||||
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
||||
|
@ -61,6 +64,7 @@
|
|||
Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such
|
||||
Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits
|
||||
Bug #7044: Changing a class' services does not affect autocalculated NPCs
|
||||
Bug #7053: Running into objects doesn't trigger GetCollidingPC
|
||||
Bug #7054: Quests aren't sorted by name
|
||||
Bug #7064: NPCs don't report crime if the player is casting offensive spells on them while sneaking
|
||||
Bug #7077: OpenMW fails to load certain particle effects in .osgt format
|
||||
|
@ -107,6 +111,7 @@
|
|||
Bug #7619: Long map notes may get cut off
|
||||
Bug #7630: Charm can be cast on creatures
|
||||
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
|
||||
Bug #7633: Groundcover should ignore non-geometry Drawables
|
||||
Bug #7636: Animations bug out when switching between 1st and 3rd person, while playing a scripted animation
|
||||
Bug #7637: Actors can sometimes move while playing scripted animations
|
||||
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
|
||||
|
@ -128,18 +133,28 @@
|
|||
Bug #7724: Guards don't help vs werewolves
|
||||
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
|
||||
Bug #7742: Governing attribute training limit should use the modified attribute
|
||||
Bug #7753: Editor: Actors Don't Scale According to Their Race
|
||||
Bug #7758: Water walking is not taken into account to compute path cost on the water
|
||||
Bug #7761: Rain and ambient loop sounds are mutually exclusive
|
||||
Bug #7765: OpenMW-CS: Touch Record option is broken
|
||||
Bug #7769: Sword of the Perithia: Broken NPCs
|
||||
Bug #7770: Sword of the Perithia: Script execution failure
|
||||
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
||||
Bug #7785: OpenMW-CS initialising Skill and Attribute fields to 0 instead of -1 on non-FortifyStat spells
|
||||
Bug #7794: Fleeing NPCs name tooltip doesn't appear
|
||||
Bug #7796: Absorbed enchantments don't restore magicka
|
||||
Bug #7832: Ingredient tooltips show magnitude for Fortify Maximum Magicka effect
|
||||
Bug #7840: First run of the launcher doesn't save viewing distance as the default value
|
||||
Feature #2566: Handle NAM9 records for manual cell references
|
||||
Feature #3537: Shader-based water ripples
|
||||
Feature #5173: Support for NiFogProperty
|
||||
Feature #5492: Let rain and snow collide with statics
|
||||
Feature #5926: Refraction based on water depth
|
||||
Feature #5944: Option to use camera as sound listener
|
||||
Feature #6149: Dehardcode Lua API_REVISION
|
||||
Feature #6152: Playing music via lua scripts
|
||||
Feature #6188: Specular lighting from point light sources
|
||||
Feature #6411: Support translations in openmw-launcher
|
||||
Feature #6447: Add LOD support to Object Paging
|
||||
Feature #6491: Add support for Qt6
|
||||
Feature #6556: Lua API for sounds
|
||||
|
@ -166,13 +181,18 @@
|
|||
Feature #7546: Start the game on Fredas
|
||||
Feature #7554: Controller binding for tab for menu navigation
|
||||
Feature #7568: Uninterruptable scripted music
|
||||
Feature #7606: Launcher: allow Shift-select in Archives tab
|
||||
Feature #7608: Make the missing dependencies warning when loading a savegame more helpful
|
||||
Feature #7618: Show the player character's health in the save details
|
||||
Feature #7625: Add some missing console error outputs
|
||||
Feature #7634: Support NiParticleBomb
|
||||
Feature #7648: Lua Save game API
|
||||
Feature #7652: Sort inactive post processing shaders list properly
|
||||
Feature #7698: Implement sAbsorb, sDamage, sDrain, sFortify and sRestore
|
||||
Feature #7709: Improve resolution selection in Launcher
|
||||
Feature #7792: Support Timescale Clouds
|
||||
Feature #7795: Support MaxNumberRipples INI setting
|
||||
Feature #7805: Lua Menu context
|
||||
Task #5896: Do not use deprecated MyGUI properties
|
||||
Task #6624: Drop support for saves made prior to 0.45
|
||||
Task #7113: Move from std::atoi to std::from_char
|
||||
|
|
|
@ -22,7 +22,7 @@ declare -a CMAKE_CONF_OPTS=(
|
|||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_INSTALL_PREFIX=install
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DBUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-OFF}"
|
||||
-DUSE_SYSTEM_TINYXML=ON
|
||||
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
||||
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
||||
|
|
11
CI/check_qt_translations.sh
Executable file
11
CI/check_qt_translations.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
set -o pipefail
|
||||
|
||||
LUPDATE="${LUPDATE:-lupdate}"
|
||||
|
||||
${LUPDATE:?} apps/wizard -ts files/lang/wizard_*.ts
|
||||
${LUPDATE:?} apps/launcher -ts files/lang/launcher_*.ts
|
||||
${LUPDATE:?} components/contentselector components/process -ts files/lang/components_*.ts
|
||||
|
||||
! (git diff --name-only | grep -q "^") || (echo -e "\033[0;31mBuild a 'translations' CMake target to update Qt localization for these files:\033[0;0m"; git diff --name-only | xargs -i echo -e "\033[0;31m{}\033[0;0m"; exit -1)
|
|
@ -33,9 +33,10 @@ declare -rA GROUPED_DEPS=(
|
|||
libboost-system-dev libboost-iostreams-dev
|
||||
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
|
||||
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
|
||||
librecast-dev libsqlite3-dev ca-certificates libicu-dev libyaml-cpp-dev
|
||||
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
|
||||
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
|
||||
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
|
||||
libyaml-cpp-dev
|
||||
"
|
||||
|
||||
# These dependencies can alternatively be built and linked statically.
|
||||
|
@ -99,6 +100,12 @@ declare -rA GROUPED_DEPS=(
|
|||
clang-format-14
|
||||
git-core
|
||||
"
|
||||
|
||||
[openmw-qt-translations]="
|
||||
qttools5-dev
|
||||
qttools5-dev-tools
|
||||
git-core
|
||||
"
|
||||
)
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
|
|
|
@ -19,6 +19,14 @@ if(OPENMW_GL4ES_MANUAL_INIT)
|
|||
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
||||
endif()
|
||||
|
||||
if (APPLE OR WIN32)
|
||||
set(DEPLOY_QT_TRANSLATIONS_DEFAULT ON)
|
||||
else ()
|
||||
set(DEPLOY_QT_TRANSLATIONS_DEFAULT OFF)
|
||||
endif ()
|
||||
|
||||
option(DEPLOY_QT_TRANSLATIONS "Deploy standard Qt translations to resources folder. Needed when OpenMW applications are deployed with Qt libraries" ${DEPLOY_QT_TRANSLATIONS_DEFAULT})
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_OPENMW "Build OpenMW" ON)
|
||||
option(BUILD_LAUNCHER "Build Launcher" ON)
|
||||
|
@ -72,7 +80,7 @@ message(STATUS "Configuring OpenMW...")
|
|||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 51)
|
||||
set(OPENMW_LUA_API_REVISION 54)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
@ -224,9 +232,9 @@ find_package(LZ4 REQUIRED)
|
|||
if (USE_QT)
|
||||
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
|
||||
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
|
||||
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL REQUIRED)
|
||||
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools REQUIRED)
|
||||
else()
|
||||
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets REQUIRED)
|
||||
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools REQUIRED)
|
||||
endif()
|
||||
message(STATUS "Using Qt${QT_VERSION}")
|
||||
endif()
|
||||
|
@ -1074,3 +1082,78 @@ if (DOXYGEN_FOUND)
|
|||
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
|
||||
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
|
||||
endif ()
|
||||
|
||||
# Qt localization
|
||||
if (USE_QT)
|
||||
file(GLOB LAUNCHER_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/launcher_*.ts)
|
||||
file(GLOB WIZARD_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/wizard_*.ts)
|
||||
file(GLOB COMPONENTS_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/components_*.ts)
|
||||
get_target_property(QT_LUPDATE_EXECUTABLE Qt::lupdate IMPORTED_LOCATION)
|
||||
add_custom_target(translations
|
||||
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/components
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
|
||||
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/wizard
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS
|
||||
|
||||
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/launcher
|
||||
VERBATIM
|
||||
COMMAND_EXPAND_LISTS)
|
||||
|
||||
if (BUILD_LAUNCHER OR BUILD_WIZARD)
|
||||
if (APPLE)
|
||||
set(QT_OPENMW_TRANSLATIONS_PATH "${APP_BUNDLE_DIR}/Contents/Resources/resources/translations")
|
||||
else ()
|
||||
get_generator_is_multi_config(multi_config)
|
||||
if (multi_config)
|
||||
set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/$<CONFIG>/resources/translations")
|
||||
else ()
|
||||
set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
file(GLOB TS_FILES ${COMPONENTS_TS_FILES})
|
||||
|
||||
if (BUILD_LAUNCHER)
|
||||
set(TS_FILES ${TS_FILES} ${LAUNCHER_TS_FILES})
|
||||
endif ()
|
||||
|
||||
if (BUILD_WIZARD)
|
||||
set(TS_FILES ${TS_FILES} ${WIZARD_TS_FILES})
|
||||
endif ()
|
||||
|
||||
qt_add_translation(QM_FILES ${TS_FILES} OPTIONS -silent)
|
||||
|
||||
if (DEPLOY_QT_TRANSLATIONS)
|
||||
# Once we set a Qt 6.2.0 as a minimum required version, we may use "qtpaths --qt-query" instead.
|
||||
get_target_property(QT_QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
|
||||
execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS
|
||||
OUTPUT_VARIABLE QT_TRANSLATIONS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
foreach(QM_FILE ${QM_FILES})
|
||||
get_filename_component(QM_BASENAME ${QM_FILE} NAME)
|
||||
string(REGEX REPLACE "[^_]+_(.*)\\.qm" "\\1" LANG_NAME ${QM_BASENAME})
|
||||
if (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||
elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||
else ()
|
||||
message(FATAL_ERROR "Qt translations for '${LANG_NAME}' locale are not found in the '${QT_TRANSLATIONS_DIR}' folder.")
|
||||
endif ()
|
||||
endforeach(QM_FILE)
|
||||
|
||||
list(REMOVE_DUPLICATES QM_FILES)
|
||||
endif ()
|
||||
|
||||
add_custom_target(qm-files
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||
DEPENDS ${QM_FILES}
|
||||
COMMENT "Copy *.qm files to resources folder")
|
||||
endif ()
|
||||
endif()
|
||||
|
|
|
@ -76,10 +76,6 @@ namespace
|
|||
bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")->multitoken()->composing(),
|
||||
"set fallback BSA archives (later archives have higher priority)");
|
||||
|
||||
addOption("resources",
|
||||
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
||||
"set resources directory");
|
||||
|
||||
addOption("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
||||
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ Allowed options)");
|
|||
addOption("name,n", bpo::value<std::string>(), "Show only the record with this name. Only affects dump mode.");
|
||||
addOption("plain,p",
|
||||
"Print contents of dialogs, books and scripts. "
|
||||
"(skipped by default)"
|
||||
"(skipped by default) "
|
||||
"Only affects dump mode.");
|
||||
addOption("quiet,q", "Suppress all record information. Useful for speed tests.");
|
||||
addOption("loadcells,C", "Browse through contents of all cells.");
|
||||
|
@ -390,7 +390,7 @@ namespace
|
|||
|
||||
if (!quiet && interested)
|
||||
{
|
||||
std::cout << "\nRecord: " << n.toStringView() << " '" << record->getId() << "'\n"
|
||||
std::cout << "\nRecord: " << n.toStringView() << " " << record->getId() << "\n"
|
||||
<< "Record flags: " << recordFlags(record->getFlags()) << '\n';
|
||||
record->print();
|
||||
}
|
||||
|
|
|
@ -464,7 +464,8 @@ namespace EsmTool
|
|||
{
|
||||
std::cout << " Name: " << mData.mName << std::endl;
|
||||
std::cout << " Model: " << mData.mModel << std::endl;
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
if (!mData.mScript.empty())
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||
}
|
||||
|
||||
|
@ -516,7 +517,8 @@ namespace EsmTool
|
|||
std::cout << " Name: " << mData.mName << std::endl;
|
||||
std::cout << " Model: " << mData.mModel << std::endl;
|
||||
std::cout << " Icon: " << mData.mIcon << std::endl;
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
if (!mData.mScript.empty())
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")"
|
||||
<< std::endl;
|
||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||
|
@ -679,7 +681,8 @@ namespace EsmTool
|
|||
{
|
||||
std::cout << " Name: " << mData.mName << std::endl;
|
||||
std::cout << " Model: " << mData.mModel << std::endl;
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
if (!mData.mScript.empty())
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
std::cout << " Flags: " << creatureFlags((int)mData.mFlags) << std::endl;
|
||||
std::cout << " Blood Type: " << mData.mBloodType + 1 << std::endl;
|
||||
std::cout << " Original: " << mData.mOriginal << std::endl;
|
||||
|
@ -719,9 +722,6 @@ namespace EsmTool
|
|||
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
||||
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
||||
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
||||
std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl;
|
||||
std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl;
|
||||
std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl;
|
||||
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
||||
|
||||
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
||||
|
@ -747,7 +747,8 @@ namespace EsmTool
|
|||
{
|
||||
std::cout << " Name: " << mData.mName << std::endl;
|
||||
std::cout << " Model: " << mData.mModel << std::endl;
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
if (!mData.mScript.empty())
|
||||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
|
||||
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
|
||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||
|
@ -1111,9 +1112,6 @@ namespace EsmTool
|
|||
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
||||
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
||||
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
||||
std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl;
|
||||
std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl;
|
||||
std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl;
|
||||
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
||||
|
||||
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
||||
|
@ -1338,28 +1336,26 @@ namespace EsmTool
|
|||
template <>
|
||||
void Record<CellState>::print()
|
||||
{
|
||||
std::cout << " Id:" << std::endl;
|
||||
std::cout << " CellId: " << mData.mCellState.mId << std::endl;
|
||||
std::cout << " Index:" << std::endl;
|
||||
std::cout << " WaterLevel: " << mData.mCellState.mWaterLevel << std::endl;
|
||||
std::cout << " HasFogOfWar: " << mData.mCellState.mHasFogOfWar << std::endl;
|
||||
std::cout << " LastRespawn:" << std::endl;
|
||||
std::cout << " Cell Id: \"" << mData.mCellState.mId.toString() << "\"" << std::endl;
|
||||
std::cout << " Water Level: " << mData.mCellState.mWaterLevel << std::endl;
|
||||
std::cout << " Has Fog Of War: " << mData.mCellState.mHasFogOfWar << std::endl;
|
||||
std::cout << " Last Respawn:" << std::endl;
|
||||
std::cout << " Day:" << mData.mCellState.mLastRespawn.mDay << std::endl;
|
||||
std::cout << " Hour:" << mData.mCellState.mLastRespawn.mHour << std::endl;
|
||||
if (mData.mCellState.mHasFogOfWar)
|
||||
{
|
||||
std::cout << " NorthMarkerAngle: " << mData.mFogState.mNorthMarkerAngle << std::endl;
|
||||
std::cout << " North Marker Angle: " << mData.mFogState.mNorthMarkerAngle << std::endl;
|
||||
std::cout << " Bounds:" << std::endl;
|
||||
std::cout << " MinX: " << mData.mFogState.mBounds.mMinX << std::endl;
|
||||
std::cout << " MinY: " << mData.mFogState.mBounds.mMinY << std::endl;
|
||||
std::cout << " MaxX: " << mData.mFogState.mBounds.mMaxX << std::endl;
|
||||
std::cout << " MaxY: " << mData.mFogState.mBounds.mMaxY << std::endl;
|
||||
std::cout << " Min X: " << mData.mFogState.mBounds.mMinX << std::endl;
|
||||
std::cout << " Min Y: " << mData.mFogState.mBounds.mMinY << std::endl;
|
||||
std::cout << " Max X: " << mData.mFogState.mBounds.mMaxX << std::endl;
|
||||
std::cout << " Max Y: " << mData.mFogState.mBounds.mMaxY << std::endl;
|
||||
for (const ESM::FogTexture& fogTexture : mData.mFogState.mFogTextures)
|
||||
{
|
||||
std::cout << " FogTexture:" << std::endl;
|
||||
std::cout << " Fog Texture:" << std::endl;
|
||||
std::cout << " X: " << fogTexture.mX << std::endl;
|
||||
std::cout << " Y: " << fogTexture.mY << std::endl;
|
||||
std::cout << " ImageData: (" << fogTexture.mImageData.size() << ")" << std::endl;
|
||||
std::cout << " Image Data: (" << fogTexture.mImageData.size() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1367,7 +1363,7 @@ namespace EsmTool
|
|||
template <>
|
||||
std::string Record<ESM::Cell>::getId() const
|
||||
{
|
||||
return mData.mName;
|
||||
return std::string(); // No ID for Cell record
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -1397,9 +1393,7 @@ namespace EsmTool
|
|||
template <>
|
||||
std::string Record<CellState>::getId() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << mData.mCellState.mId;
|
||||
return stream.str();
|
||||
return std::string(); // No ID for CellState record
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
|
|
@ -71,6 +71,8 @@ openmw_add_executable(openmw-launcher
|
|||
${UI_HDRS}
|
||||
)
|
||||
|
||||
add_dependencies(openmw-launcher qm-files)
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
|
||||
endif (WIN32)
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QList>
|
||||
#include <QMessageBox>
|
||||
#include <QPair>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -162,8 +164,8 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
|
|||
connect(ui.directoryUpButton, &QPushButton::released, this, [this]() { this->moveDirectory(-1); });
|
||||
connect(ui.directoryDownButton, &QPushButton::released, this, [this]() { this->moveDirectory(1); });
|
||||
connect(ui.directoryRemoveButton, &QPushButton::released, this, [this]() { this->removeDirectory(); });
|
||||
connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchive(-1); });
|
||||
connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchive(1); });
|
||||
connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchives(-1); });
|
||||
connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchives(1); });
|
||||
connect(
|
||||
ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, [this]() { this->sortDirectories(); });
|
||||
|
||||
|
@ -218,6 +220,18 @@ void Launcher::DataFilesPage::buildView()
|
|||
&DataFilesPage::readNavMeshToolStderr);
|
||||
connect(mNavMeshToolInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
|
||||
&DataFilesPage::navMeshToolFinished);
|
||||
|
||||
buildArchiveContextMenu();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::buildArchiveContextMenu()
|
||||
{
|
||||
connect(ui.archiveListWidget, &QListWidget::customContextMenuRequested, this,
|
||||
&DataFilesPage::slotShowArchiveContextMenu);
|
||||
|
||||
mArchiveContextMenu = new QMenu(ui.archiveListWidget);
|
||||
mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems()));
|
||||
mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems()));
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::loadSettings()
|
||||
|
@ -294,7 +308,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
|||
// Display new content with custom formatting
|
||||
if (mNewDataDirs.contains(canonicalDirPath))
|
||||
{
|
||||
tooltip += "Will be added to the current profile\n";
|
||||
tooltip += tr("Will be added to the current profile");
|
||||
QFont font = item->font();
|
||||
font.setBold(true);
|
||||
font.setItalic(true);
|
||||
|
@ -312,7 +326,10 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
|||
if (mSelector->containsDataFiles(currentDir))
|
||||
{
|
||||
item->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||
tooltip += "Contains content file(s)";
|
||||
if (!tooltip.isEmpty())
|
||||
tooltip += "\n";
|
||||
|
||||
tooltip += tr("Contains content file(s)");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -707,17 +724,71 @@ void Launcher::DataFilesPage::removeDirectory()
|
|||
refreshDataFilesView();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::moveArchive(int step)
|
||||
void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos)
|
||||
{
|
||||
int selectedRow = ui.archiveListWidget->currentRow();
|
||||
QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos);
|
||||
mArchiveContextMenu->exec(globalPos);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked)
|
||||
{
|
||||
Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
|
||||
|
||||
for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems())
|
||||
{
|
||||
selectedItem->setCheckState(checkState);
|
||||
}
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotUncheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(false);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotCheckMultiSelectedItems()
|
||||
{
|
||||
setCheckStateForMultiSelectedItems(true);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::moveArchives(int step)
|
||||
{
|
||||
QList<QListWidgetItem*> selectedItems = ui.archiveListWidget->selectedItems();
|
||||
QList<QPair<int, QListWidgetItem*>> sortedItems;
|
||||
|
||||
for (QListWidgetItem* selectedItem : selectedItems)
|
||||
{
|
||||
int selectedRow = ui.archiveListWidget->row(selectedItem);
|
||||
sortedItems.append(qMakePair(selectedRow, selectedItem));
|
||||
}
|
||||
|
||||
if (step > 0)
|
||||
{
|
||||
std::sort(sortedItems.begin(), sortedItems.end(), [](auto a, auto b) { return a.first > b.first; });
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(sortedItems.begin(), sortedItems.end(), [](auto a, auto b) { return a.first < b.first; });
|
||||
}
|
||||
|
||||
for (auto i : sortedItems)
|
||||
{
|
||||
if (!moveArchive(i.second, step))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::moveArchive(QListWidgetItem* listItem, int step)
|
||||
{
|
||||
int selectedRow = ui.archiveListWidget->row(listItem);
|
||||
int newRow = selectedRow + step;
|
||||
if (selectedRow == -1 || newRow < 0 || newRow > ui.archiveListWidget->count() - 1)
|
||||
return;
|
||||
return false;
|
||||
|
||||
const auto* item = ui.archiveListWidget->takeItem(selectedRow);
|
||||
const QListWidgetItem* item = ui.archiveListWidget->takeItem(selectedRow);
|
||||
|
||||
addArchive(item->text(), item->checkState(), newRow);
|
||||
ui.archiveListWidget->setCurrentRow(newRow);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState selected, int row)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include <QDir>
|
||||
#include <QMenu>
|
||||
#include <QStringList>
|
||||
#include <QWidget>
|
||||
|
||||
|
@ -39,6 +40,7 @@ namespace Launcher
|
|||
|
||||
ContentSelectorView::ContentSelector* mSelector;
|
||||
Ui::DataFilesPage ui;
|
||||
QMenu* mArchiveContextMenu;
|
||||
|
||||
public:
|
||||
explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
|
||||
|
@ -72,9 +74,13 @@ namespace Launcher
|
|||
void addSubdirectories(bool append);
|
||||
void sortDirectories();
|
||||
void removeDirectory();
|
||||
void moveArchive(int step);
|
||||
void moveArchives(int step);
|
||||
void moveDirectory(int step);
|
||||
|
||||
void slotShowArchiveContextMenu(const QPoint& pos);
|
||||
void slotCheckMultiSelectedItems();
|
||||
void slotUncheckMultiSelectedItems();
|
||||
|
||||
void on_newProfileAction_triggered();
|
||||
void on_cloneProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
|
@ -120,7 +126,10 @@ namespace Launcher
|
|||
|
||||
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
||||
void addArchivesFromDir(const QString& dir);
|
||||
bool moveArchive(QListWidgetItem* listItem, int step);
|
||||
void buildView();
|
||||
void buildArchiveContextMenu();
|
||||
void setCheckStateForMultiSelectedItems(bool checked);
|
||||
void setProfile(int index, bool savePrevious);
|
||||
void setProfile(const QString& previous, const QString& current, bool savePrevious);
|
||||
void removeProfile(const QString& profile);
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include <components/debug/debugging.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/qtconversion.hpp>
|
||||
#include <components/l10n/qttranslations.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
@ -34,12 +35,13 @@ int runLauncher(int argc, char* argv[])
|
|||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Internationalization
|
||||
QString locale = QLocale::system().name().section('_', 0, 0);
|
||||
QString resourcesPath(".");
|
||||
if (!variables["resources"].empty())
|
||||
{
|
||||
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
|
||||
}
|
||||
|
||||
QTranslator appTranslator;
|
||||
appTranslator.load(":/translations/" + locale + ".qm");
|
||||
app.installTranslator(&appTranslator);
|
||||
l10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||
|
||||
Launcher::MainDialog mainWin(configurationManager);
|
||||
|
||||
|
|
|
@ -294,6 +294,7 @@ bool Launcher::SettingsPage::loadSettings()
|
|||
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
||||
}
|
||||
}
|
||||
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
|
||||
}
|
||||
|
||||
// Interface Changes
|
||||
|
@ -490,6 +491,9 @@ void Launcher::SettingsPage::saveSettings()
|
|||
Settings::sound().mHrtf.set(hrtfProfileSelectorComboBox->currentText().toStdString());
|
||||
else
|
||||
Settings::sound().mHrtf.set({});
|
||||
|
||||
const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked;
|
||||
Settings::sound().mCameraListener.set(cCameraListener);
|
||||
}
|
||||
|
||||
// Interface Changes
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>573</width>
|
||||
<height>384</height>
|
||||
<height>557</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
|
@ -29,6 +29,12 @@
|
|||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="dataNoteLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>note: content files that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
||||
</property>
|
||||
|
@ -41,14 +47,111 @@
|
|||
<string>Data Directories</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="dirTabLayout">
|
||||
<item row="0" column="0" rowspan="26">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="directoryListWidget">
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="32" column="0" colspan="2">
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="directoryButtons">
|
||||
<item>
|
||||
<widget class="QPushButton" name="directoryAddSubdirsButton">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Scan directories for likely data directories and append them at the end of the list.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Append</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="directoryInsertButton">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Scan directories for likely data directories and insert them above the selected position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Insert Above</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="directoryUpButton">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected directory one position up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="directoryDownButton">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected directory one position down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="directoryRemoveButton">
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove selected directory</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="directoryButtonsSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="directoryNoteLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
|
@ -61,116 +164,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="directoryAddSubdirsButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Scan directories for likely data directories and append them at the end of the list.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Append</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="directoryInsertButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Scan directories for likely data directories and insert them above the selected position</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Insert Above</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QPushButton" name="directoryUpButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected directory one position up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QPushButton" name="directoryDownButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected directory one position down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<widget class="QPushButton" name="directoryRemoveButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove selected directory</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="archiveTab">
|
||||
|
@ -178,64 +171,90 @@
|
|||
<string>Archive Files</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="archiveTabLayout">
|
||||
<item row="0" column="0" rowspan="26">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="archiveListWidget">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::CopyAction</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="archiveUpButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>33</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected archive one position up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="archiveButtons">
|
||||
<item>
|
||||
<widget class="QPushButton" name="archiveUpButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>33</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected archive one position up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="archiveDownButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>33</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected archive one position down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="archiveButtonsSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="27" column="0" colspan="2">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="archiveNoteLabel">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>note: archives that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="archiveDownButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>33</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>33</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move selected archive one position down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="navigationMeshCacheTab">
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<width>750</width>
|
||||
<height>635</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>720</width>
|
||||
<width>750</width>
|
||||
<height>635</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -148,7 +148,7 @@ QToolButton {
|
|||
<string>Display</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Allows to change graphics settings</string>
|
||||
<string>Allows to change display settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="settingsAction">
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="allowNPCToFollowOverWaterSurfaceCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</string>
|
||||
<string><html><head/><body><p>Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Always allow actors to follow over water</string>
|
||||
|
@ -206,7 +206,7 @@
|
|||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html></string>
|
||||
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored - like in Morrowind.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Classic reflected Absorb spells behavior</string>
|
||||
|
@ -524,7 +524,7 @@
|
|||
<string><html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use anti-alias alpha testing</string>
|
||||
<string>Use anti-aliased alpha testing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -663,6 +663,9 @@
|
|||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> cells</string>
|
||||
</property>
|
||||
|
@ -670,7 +673,7 @@
|
|||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.500000000000000</double>
|
||||
<double>0.125000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1320,6 +1323,16 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cameraListenerCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>In third-person view, use the camera as the sound listener instead of the player character.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use the camera as the sound listener</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
|
|
|
@ -584,7 +584,7 @@ void MwIniImporter::importGameFiles(
|
|||
reader.close();
|
||||
}
|
||||
|
||||
auto sortedFiles = dependencySort(unsortedFiles);
|
||||
auto sortedFiles = dependencySort(std::move(unsortedFiles));
|
||||
|
||||
// hard-coded dependency Morrowind - Tribunal - Bloodmoon
|
||||
if (findString(sortedFiles, "Morrowind.esm") != sortedFiles.end())
|
||||
|
|
|
@ -88,10 +88,6 @@ namespace NavMeshTool
|
|||
->composing(),
|
||||
"set fallback BSA archives (later archives have higher priority)");
|
||||
|
||||
addOption("resources",
|
||||
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
||||
"set resources directory");
|
||||
|
||||
addOption("content",
|
||||
bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
||||
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
||||
|
|
|
@ -116,9 +116,9 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
|||
|
||||
for (const auto& name : vfs.getRecursiveDirectoryIterator(""))
|
||||
{
|
||||
if (isNIF(name))
|
||||
if (isNIF(name.value()))
|
||||
{
|
||||
readNIF(archivePath, name, &vfs, quiet);
|
||||
readNIF(archivePath, name.value(), &vfs, quiet);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ else()
|
|||
set (OPENCS_OPENMW_CFG "")
|
||||
endif(APPLE)
|
||||
|
||||
add_library(openmw-cs-lib
|
||||
add_library(openmw-cs-lib STATIC
|
||||
${OPENCS_SRC}
|
||||
${OPENCS_UI_HDR}
|
||||
${OPENCS_MOC_SRC}
|
||||
|
|
|
@ -119,8 +119,6 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
|
|||
boost::program_options::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(
|
||||
Files::MaybeQuotedPathContainer::value_type(), ""));
|
||||
addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||
addOption("resources",
|
||||
boost::program_options::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"));
|
||||
addOption("fallback-archive",
|
||||
boost::program_options::value<std::vector<std::string>>()
|
||||
->default_value(std::vector<std::string>(), "fallback-archive")
|
||||
|
|
|
@ -691,15 +691,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (npc.mNpdt.mHealth != 0)
|
||||
{
|
||||
for (size_t i = 0; i < npc.mNpdt.mAttributes.size(); ++i)
|
||||
{
|
||||
if (npc.mNpdt.mAttributes[i] == 0)
|
||||
messages.add(id, ESM::Attribute::indexToRefId(i).getRefIdString() + " is equal to zero", {},
|
||||
CSMDoc::Message::Severity_Warning);
|
||||
}
|
||||
}
|
||||
|
||||
if (level <= 0)
|
||||
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
|
||||
|
|
|
@ -18,16 +18,18 @@
|
|||
#include <apps/opencs/model/world/universalid.hpp>
|
||||
|
||||
#include <components/esm3/cellref.hpp>
|
||||
#include <components/esm3/loadbody.hpp>
|
||||
#include <components/esm3/loadfact.hpp>
|
||||
|
||||
CSMTools::ReferenceCheckStage::ReferenceCheckStage(const CSMWorld::RefCollection& references,
|
||||
const CSMWorld::RefIdCollection& referencables, const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions)
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions, const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
|
||||
: mReferences(references)
|
||||
, mObjects(referencables)
|
||||
, mDataSet(referencables.getDataSet())
|
||||
, mCells(cells)
|
||||
, mFactions(factions)
|
||||
, mBodyParts(bodyparts)
|
||||
{
|
||||
mIgnoreBaseRecords = false;
|
||||
}
|
||||
|
@ -49,9 +51,11 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages& message
|
|||
else
|
||||
{
|
||||
// Check for non existing referenced object
|
||||
if (mObjects.searchId(cellRef.mRefID) == -1)
|
||||
if (mObjects.searchId(cellRef.mRefID) == -1 && mBodyParts.searchId(cellRef.mRefID) == -1)
|
||||
{
|
||||
messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID.getRefIdString() + "'", "",
|
||||
CSMDoc::Message::Severity_Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if reference charge is valid for it's proper referenced type
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
namespace ESM
|
||||
{
|
||||
struct BodyPart;
|
||||
struct Faction;
|
||||
}
|
||||
|
||||
|
@ -29,7 +30,8 @@ namespace CSMTools
|
|||
{
|
||||
public:
|
||||
ReferenceCheckStage(const CSMWorld::RefCollection& references, const CSMWorld::RefIdCollection& referencables,
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells, const CSMWorld::IdCollection<ESM::Faction>& factions);
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells, const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);
|
||||
|
||||
void perform(int stage, CSMDoc::Messages& messages) override;
|
||||
int setup() override;
|
||||
|
@ -40,6 +42,7 @@ namespace CSMTools
|
|||
const CSMWorld::RefIdData& mDataSet;
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
|
||||
bool mIgnoreBaseRecords;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -105,8 +105,8 @@ CSMDoc::OperationHolder* CSMTools::Tools::getVerifier()
|
|||
mData.getFactions(), mData.getScripts(), mData.getResources(CSMWorld::UniversalId::Type_Meshes),
|
||||
mData.getResources(CSMWorld::UniversalId::Type_Icons), mData.getBodyParts()));
|
||||
|
||||
mVerifierOperation->appendStage(new ReferenceCheckStage(
|
||||
mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
|
||||
mVerifierOperation->appendStage(new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(),
|
||||
mData.getCells(), mData.getFactions(), mData.getBodyParts()));
|
||||
|
||||
mVerifierOperation->appendStage(new ScriptCheckStage(mDocument));
|
||||
|
||||
|
|
|
@ -67,6 +67,11 @@ namespace CSMWorld
|
|||
return mMaleParts[ESM::getMeshPart(index)];
|
||||
}
|
||||
|
||||
const osg::Vec2f& ActorAdapter::RaceData::getGenderWeightHeight(bool isFemale)
|
||||
{
|
||||
return isFemale ? mWeightsHeights.mFemaleWeightHeight : mWeightsHeights.mMaleWeightHeight;
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::hasDependency(const ESM::RefId& id) const
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
|
@ -90,10 +95,11 @@ namespace CSMWorld
|
|||
mDependencies.emplace(id);
|
||||
}
|
||||
|
||||
void ActorAdapter::RaceData::reset_data(const ESM::RefId& id, bool isBeast)
|
||||
void ActorAdapter::RaceData::reset_data(const ESM::RefId& id, const WeightsHeights& raceStats, bool isBeast)
|
||||
{
|
||||
mId = id;
|
||||
mIsBeast = isBeast;
|
||||
mWeightsHeights = raceStats;
|
||||
for (auto& str : mFemaleParts)
|
||||
str = ESM::RefId();
|
||||
for (auto& str : mMaleParts)
|
||||
|
@ -163,6 +169,11 @@ namespace CSMWorld
|
|||
return it->second.first;
|
||||
}
|
||||
|
||||
const osg::Vec2f& ActorAdapter::ActorData::getRaceWeightHeight() const
|
||||
{
|
||||
return mRaceData->getGenderWeightHeight(isFemale());
|
||||
}
|
||||
|
||||
bool ActorAdapter::ActorData::hasDependency(const ESM::RefId& id) const
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
|
@ -504,7 +515,11 @@ namespace CSMWorld
|
|||
}
|
||||
|
||||
auto& race = raceRecord.get();
|
||||
data->reset_data(id, race.mData.mFlags & ESM::Race::Beast);
|
||||
|
||||
WeightsHeights scaleStats = { osg::Vec2f(race.mData.mMaleWeight, race.mData.mMaleHeight),
|
||||
osg::Vec2f(race.mData.mFemaleWeight, race.mData.mFemaleHeight) };
|
||||
|
||||
data->reset_data(id, scaleStats, race.mData.mFlags & ESM::Race::Beast);
|
||||
|
||||
// Setup body parts
|
||||
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
||||
|
|
|
@ -41,6 +41,12 @@ namespace CSMWorld
|
|||
/// Tracks unique strings
|
||||
using RefIdSet = std::unordered_set<ESM::RefId>;
|
||||
|
||||
struct WeightsHeights
|
||||
{
|
||||
osg::Vec2f mMaleWeightHeight;
|
||||
osg::Vec2f mFemaleWeightHeight;
|
||||
};
|
||||
|
||||
/// Contains base race data shared between actors
|
||||
class RaceData
|
||||
{
|
||||
|
@ -57,6 +63,8 @@ namespace CSMWorld
|
|||
const ESM::RefId& getFemalePart(ESM::PartReferenceType index) const;
|
||||
/// Retrieves the associated body part
|
||||
const ESM::RefId& getMalePart(ESM::PartReferenceType index) const;
|
||||
|
||||
const osg::Vec2f& getGenderWeightHeight(bool isFemale);
|
||||
/// Checks if the race has a data dependency
|
||||
bool hasDependency(const ESM::RefId& id) const;
|
||||
|
||||
|
@ -67,7 +75,8 @@ namespace CSMWorld
|
|||
/// Marks an additional dependency
|
||||
void addOtherDependency(const ESM::RefId& id);
|
||||
/// Clears parts and dependencies
|
||||
void reset_data(const ESM::RefId& raceId, bool isBeast = false);
|
||||
void reset_data(const ESM::RefId& raceId,
|
||||
const WeightsHeights& raceStats = { osg::Vec2f(1.f, 1.f), osg::Vec2f(1.f, 1.f) }, bool isBeast = false);
|
||||
|
||||
private:
|
||||
bool handles(ESM::PartReferenceType type) const;
|
||||
|
@ -75,6 +84,7 @@ namespace CSMWorld
|
|||
bool mIsBeast;
|
||||
RacePartList mFemaleParts;
|
||||
RacePartList mMaleParts;
|
||||
WeightsHeights mWeightsHeights;
|
||||
RefIdSet mDependencies;
|
||||
};
|
||||
using RaceDataPtr = std::shared_ptr<RaceData>;
|
||||
|
@ -96,6 +106,8 @@ namespace CSMWorld
|
|||
std::string getSkeleton() const;
|
||||
/// Retrieves the associated actor part
|
||||
ESM::RefId getPart(ESM::PartReferenceType index) const;
|
||||
|
||||
const osg::Vec2f& getRaceWeightHeight() const;
|
||||
/// Checks if the actor has a data dependency
|
||||
bool hasDependency(const ESM::RefId& id) const;
|
||||
|
||||
|
|
|
@ -585,19 +585,22 @@ namespace CSMWorld
|
|||
void set(Record<ESXRecordT>& record, const QVariant& data) override
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
float bodyAttr = std::clamp(data.toFloat(), 0.5f, 2.0f);
|
||||
|
||||
if (mWeight)
|
||||
{
|
||||
if (mMale)
|
||||
record2.mData.mMaleWeight = data.toFloat();
|
||||
record2.mData.mMaleWeight = bodyAttr;
|
||||
else
|
||||
record2.mData.mFemaleWeight = data.toFloat();
|
||||
record2.mData.mFemaleWeight = bodyAttr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mMale)
|
||||
record2.mData.mMaleHeight = data.toFloat();
|
||||
record2.mData.mMaleHeight = bodyAttr;
|
||||
else
|
||||
record2.mData.mFemaleHeight = data.toFloat();
|
||||
record2.mData.mFemaleHeight = bodyAttr;
|
||||
}
|
||||
record.setModified(record2);
|
||||
}
|
||||
|
|
|
@ -385,6 +385,26 @@ namespace CSMWorld
|
|||
case 0:
|
||||
{
|
||||
effect.mEffectID = static_cast<short>(value.toInt());
|
||||
switch (effect.mEffectID)
|
||||
{
|
||||
case ESM::MagicEffect::DrainSkill:
|
||||
case ESM::MagicEffect::DamageSkill:
|
||||
case ESM::MagicEffect::RestoreSkill:
|
||||
case ESM::MagicEffect::FortifySkill:
|
||||
case ESM::MagicEffect::AbsorbSkill:
|
||||
effect.mAttribute = -1;
|
||||
break;
|
||||
case ESM::MagicEffect::DrainAttribute:
|
||||
case ESM::MagicEffect::DamageAttribute:
|
||||
case ESM::MagicEffect::RestoreAttribute:
|
||||
case ESM::MagicEffect::FortifyAttribute:
|
||||
case ESM::MagicEffect::AbsorbAttribute:
|
||||
effect.mSkill = -1;
|
||||
break;
|
||||
default:
|
||||
effect.mSkill = -1;
|
||||
effect.mAttribute = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
|
|
|
@ -30,18 +30,18 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char* const* e
|
|||
|
||||
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
|
||||
{
|
||||
if (filepath.size() < baseSize + 1 || filepath.substr(0, baseSize) != mBaseDirectory
|
||||
|| (filepath[baseSize] != '/' && filepath[baseSize] != '\\'))
|
||||
const std::string_view view = filepath.view();
|
||||
if (view.size() < baseSize + 1 || !view.starts_with(mBaseDirectory) || view[baseSize] != '/')
|
||||
continue;
|
||||
|
||||
if (extensions)
|
||||
{
|
||||
std::string::size_type extensionIndex = filepath.find_last_of('.');
|
||||
const auto extensionIndex = view.find_last_of('.');
|
||||
|
||||
if (extensionIndex == std::string::npos)
|
||||
if (extensionIndex == std::string_view::npos)
|
||||
continue;
|
||||
|
||||
std::string extension = filepath.substr(extensionIndex + 1);
|
||||
std::string_view extension = view.substr(extensionIndex + 1);
|
||||
|
||||
int i = 0;
|
||||
|
||||
|
@ -53,10 +53,9 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char* const* e
|
|||
continue;
|
||||
}
|
||||
|
||||
std::string file = filepath.substr(baseSize + 1);
|
||||
std::string file(view.substr(baseSize + 1));
|
||||
mFiles.push_back(file);
|
||||
std::replace(file.begin(), file.end(), '\\', '/');
|
||||
mIndex.insert(std::make_pair(Misc::StringUtils::lowerCase(file), static_cast<int>(mFiles.size()) - 1));
|
||||
mIndex.emplace(std::move(file), static_cast<int>(mFiles.size()) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <osg/Group>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/Vec3d>
|
||||
|
||||
#include <apps/opencs/model/world/actoradapter.hpp>
|
||||
#include <apps/opencs/model/world/idcollection.hpp>
|
||||
|
@ -29,7 +30,7 @@ namespace CSVRender
|
|||
Actor::Actor(const ESM::RefId& id, CSMWorld::Data& data)
|
||||
: mId(id)
|
||||
, mData(data)
|
||||
, mBaseNode(new osg::Group())
|
||||
, mBaseNode(new osg::PositionAttitudeTransform())
|
||||
, mSkeleton(nullptr)
|
||||
{
|
||||
mActorData = mData.getActorAdapter()->getActorData(mId);
|
||||
|
@ -60,6 +61,10 @@ namespace CSVRender
|
|||
|
||||
// Attach parts to skeleton
|
||||
loadBodyParts();
|
||||
|
||||
const osg::Vec2f& attributes = mActorData->getRaceWeightHeight();
|
||||
|
||||
mBaseNode->setScale(osg::Vec3d(attributes.x(), attributes.x(), attributes.y()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <string_view>
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <QObject>
|
||||
|
@ -59,7 +60,7 @@ namespace CSVRender
|
|||
CSMWorld::Data& mData;
|
||||
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
||||
|
||||
osg::ref_ptr<osg::Group> mBaseNode;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||
SceneUtil::Skeleton* mSkeleton;
|
||||
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||
};
|
||||
|
|
|
@ -61,10 +61,14 @@ add_openmw_dir (mwscript
|
|||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
||||
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings itemdata
|
||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal
|
||||
worker magicbindings factionbindings classbindings animationbindings
|
||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
||||
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||
classbindings itemdata inputprocessor animationbindings birthsignbindings
|
||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
||||
types/clothing types/levelledlist types/terminal
|
||||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
|
@ -84,7 +88,7 @@ add_openmw_dir (mwworld
|
|||
|
||||
add_openmw_dir (mwphysics
|
||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
||||
contacttestresultcallback stepper movementsolver projectile
|
||||
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
||||
)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <components/debug/gldebug.hpp>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/strings/format.hpp>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
@ -109,10 +110,23 @@ namespace
|
|||
profiler.removeUserStatsLine(" -Async");
|
||||
}
|
||||
|
||||
struct ScheduleNonDialogMessageBox
|
||||
struct ScreenCaptureMessageBox
|
||||
{
|
||||
void operator()(std::string message) const
|
||||
void operator()(std::string filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->scheduleMessageBox(
|
||||
"#{OMWEngine:ScreenshotFailed}", MWGui::ShowInDialogueMode_Never);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string messageFormat
|
||||
= MWBase::Environment::get().getL10nManager()->getMessage("OMWEngine", "ScreenshotMade");
|
||||
|
||||
std::string message = Misc::StringUtils::format(messageFormat, filePath);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->scheduleMessageBox(
|
||||
std::move(message), MWGui::ShowInDialogueMode_Never);
|
||||
}
|
||||
|
@ -717,9 +731,8 @@ void OMW::Engine::prepareEngine()
|
|||
mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation(mWorkQueue,
|
||||
new SceneUtil::WriteScreenshotToFileOperation(mCfgMgr.getScreenshotPath(),
|
||||
Settings::general().mScreenshotFormat,
|
||||
Settings::general().mNotifyOnSavedScreenshot
|
||||
? std::function<void(std::string)>(ScheduleNonDialogMessageBox{})
|
||||
: std::function<void(std::string)>(IgnoreString{})));
|
||||
Settings::general().mNotifyOnSavedScreenshot ? std::function<void(std::string)>(ScreenCaptureMessageBox{})
|
||||
: std::function<void(std::string)>(IgnoreString{})));
|
||||
|
||||
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
||||
|
||||
|
@ -890,8 +903,8 @@ void OMW::Engine::prepareEngine()
|
|||
<< 100 * static_cast<double>(result.second) / result.first << "%)";
|
||||
}
|
||||
|
||||
mLuaManager->init();
|
||||
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath());
|
||||
mLuaManager->init();
|
||||
|
||||
// starts a separate lua thread if "lua num threads" > 0
|
||||
mLuaWorker = std::make_unique<MWLua::Worker>(*mLuaManager, *mViewer);
|
||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
|||
|
||||
virtual void newGameStarted() = 0;
|
||||
virtual void gameLoaded() = 0;
|
||||
virtual void gameEnded() = 0;
|
||||
virtual void noGame() = 0;
|
||||
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;
|
||||
|
@ -64,8 +66,10 @@ namespace MWBase
|
|||
virtual void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) = 0;
|
||||
virtual void playAnimation(const MWWorld::Ptr& object, const std::string& groupname,
|
||||
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||
std::string_view start, std::string_view stop, float startpoint, size_t loops, bool loopfallback)
|
||||
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
|
||||
= 0;
|
||||
virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
|
||||
virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
|
||||
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
||||
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||
|
@ -79,6 +83,12 @@ namespace MWBase
|
|||
|
||||
struct InputEvent
|
||||
{
|
||||
struct WheelChange
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
KeyPressed,
|
||||
|
@ -89,8 +99,11 @@ namespace MWBase
|
|||
TouchPressed,
|
||||
TouchReleased,
|
||||
TouchMoved,
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseWheel,
|
||||
} mType;
|
||||
std::variant<SDL_Keysym, int, SDLUtil::TouchEvent> mValue;
|
||||
std::variant<SDL_Keysym, int, SDLUtil::TouchEvent, WheelChange> mValue;
|
||||
};
|
||||
virtual void inputEvent(const InputEvent& event) = 0;
|
||||
|
||||
|
|
|
@ -110,7 +110,9 @@ namespace MWBase
|
|||
virtual bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
|
||||
|
||||
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
|
||||
virtual void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
|
||||
virtual void startCombat(
|
||||
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
|
||||
= 0;
|
||||
|
||||
/// Removes an actor and its allies from combat with the actor's targets.
|
||||
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
|
||||
|
@ -171,7 +173,7 @@ namespace MWBase
|
|||
///< Forces an object to refresh its animation state.
|
||||
|
||||
virtual bool playAnimationGroup(
|
||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number = 1, bool scripted = false)
|
||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, uint32_t number = 1, bool scripted = false)
|
||||
= 0;
|
||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||
/// in the scene should be ignored.
|
||||
|
@ -180,8 +182,8 @@ namespace MWBase
|
|||
/// \param number How many times the animation should be run
|
||||
/// \param scripted Whether the animation should be treated as a scripted animation.
|
||||
/// \return Success or error
|
||||
virtual bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
||||
std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||
virtual bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, uint32_t loops,
|
||||
float speed, std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||
= 0;
|
||||
///< Lua variant of playAnimationGroup. The mode parameter is omitted
|
||||
/// and forced to 0. modes 1 and 2 can be emulated by doing clearAnimationQueue() and
|
||||
|
|
|
@ -44,6 +44,9 @@ namespace MWBase
|
|||
|
||||
virtual void askLoadRecent() = 0;
|
||||
|
||||
virtual void requestNewGame() = 0;
|
||||
virtual void requestLoad(const std::filesystem::path& filepath) = 0;
|
||||
|
||||
virtual State getState() const = 0;
|
||||
|
||||
virtual void newGame(bool bypass = false) = 0;
|
||||
|
|
|
@ -166,7 +166,8 @@ namespace MWBase
|
|||
|
||||
virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0;
|
||||
virtual MWWorld::Ptr getConsoleSelectedObject() const = 0;
|
||||
virtual void setConsoleMode(const std::string& mode) = 0;
|
||||
virtual void setConsoleMode(std::string_view mode) = 0;
|
||||
virtual const std::string& getConsoleMode() = 0;
|
||||
|
||||
static constexpr std::string_view sConsoleColor_Default = "#FFFFFF";
|
||||
static constexpr std::string_view sConsoleColor_Error = "#FF2222";
|
||||
|
|
|
@ -304,7 +304,7 @@ namespace MWBase
|
|||
virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;
|
||||
|
||||
virtual bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
|
||||
bool ignorePlayer, bool ignoreActors)
|
||||
bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList = {})
|
||||
= 0;
|
||||
|
||||
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace MWClass
|
|||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||
}
|
||||
|
||||
std::string Activator::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Activator::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Activator>(ptr);
|
||||
}
|
||||
|
@ -141,15 +141,14 @@ namespace MWClass
|
|||
|
||||
ESM::RefId Activator::getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const
|
||||
{
|
||||
const std::string model
|
||||
= getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
|
||||
// Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
|
||||
const std::string_view model = getModel(ptr);
|
||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||
const ESM::RefId* creatureId = nullptr;
|
||||
|
||||
for (const ESM::Creature& iter : store.get<ESM::Creature>())
|
||||
{
|
||||
if (!iter.mModel.empty()
|
||||
&& Misc::StringUtils::ciEqual(model, Misc::ResourceHelpers::correctMeshPath(iter.mModel)))
|
||||
if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, iter.mModel))
|
||||
{
|
||||
creatureId = !iter.mOriginal.empty() ? &iter.mOriginal : &iter.mId;
|
||||
break;
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
||||
///< Generate action for activation
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool useAnim() const override;
|
||||
///< Whether or not to use animated variant of model (default false)
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Apparatus::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Apparatus::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Apparatus>(ptr);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Armor::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Armor::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Armor>(ptr);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace MWClass
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string BodyPart::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view BodyPart::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::BodyPart>(ptr);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MWClass
|
|||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Book::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Book::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Book>(ptr);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -4,22 +4,16 @@
|
|||
#include "../mwworld/livecellref.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace MWClass
|
||||
{
|
||||
template <class Class>
|
||||
std::string getClassModel(const MWWorld::ConstPtr& ptr)
|
||||
std::string_view getClassModel(const MWWorld::ConstPtr& ptr)
|
||||
{
|
||||
const MWWorld::LiveCellRef<Class>* ref = ptr.get<Class>();
|
||||
|
||||
if (!ref->mBase->mModel.empty())
|
||||
return Misc::ResourceHelpers::correctMeshPath(ref->mBase->mModel);
|
||||
|
||||
return {};
|
||||
return ref->mBase->mModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Clothing::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Clothing::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Clothing>(ptr);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ namespace MWClass
|
|||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||
}
|
||||
|
||||
std::string Container::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Container::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Container>(ptr);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace MWClass
|
|||
|
||||
void respawn(const MWWorld::Ptr& ptr) const override;
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool useAnim() const override;
|
||||
|
||||
|
|
|
@ -178,14 +178,14 @@ namespace MWClass
|
|||
objects.insertCreature(ptr, model, hasInventoryStore(ptr));
|
||||
}
|
||||
|
||||
std::string Creature::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Creature::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Creature>(ptr);
|
||||
}
|
||||
|
||||
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const
|
||||
{
|
||||
std::string model = getModel(ptr);
|
||||
std::string_view model = getModel(ptr);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
|
||||
|
@ -474,20 +474,17 @@ namespace MWClass
|
|||
}
|
||||
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
|
||||
|
||||
const bool isInCombat = aiSequence.isInCombat();
|
||||
if (stats.isDead())
|
||||
{
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation && !isInCombat)
|
||||
// by default user can loot non-fighting actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// otherwise wait until death animation
|
||||
if (stats.isDeathAnimationFinished())
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
}
|
||||
else if ((!isInCombat || aiSequence.isFleeing()) && !stats.getKnockedDown())
|
||||
else if (!stats.getKnockedDown())
|
||||
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
||||
|
||||
// Tribunal and some mod companions oddly enough must use open action as fallback
|
||||
|
@ -651,13 +648,13 @@ namespace MWClass
|
|||
|
||||
if (sounds.empty())
|
||||
{
|
||||
const std::string model = getModel(ptr);
|
||||
const std::string_view model = getModel(ptr);
|
||||
if (!model.empty())
|
||||
{
|
||||
for (const ESM::Creature& creature : store.get<ESM::Creature>())
|
||||
{
|
||||
if (creature.mId != ourId && creature.mOriginal != ourId && !creature.mModel.empty()
|
||||
&& Misc::StringUtils::ciEqual(model, Misc::ResourceHelpers::correctMeshPath(creature.mModel)))
|
||||
&& Misc::StringUtils::ciEqual(model, creature.mModel))
|
||||
{
|
||||
const ESM::RefId& fallbackId = !creature.mOriginal.empty() ? creature.mOriginal : creature.mId;
|
||||
sound = store.get<ESM::SoundGenerator>().begin();
|
||||
|
|
|
@ -105,9 +105,9 @@ namespace MWClass
|
|||
|
||||
float getMaxSpeed(const MWWorld::Ptr& ptr) const override;
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace MWClass
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string Door::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Door::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Door>(ptr);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace MWClass
|
|||
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
MWWorld::DoorState getDoorState(const MWWorld::ConstPtr& ptr) const override;
|
||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||
|
|
|
@ -96,14 +96,14 @@ namespace MWClass
|
|||
|
||||
std::string_view getName(const MWWorld::ConstPtr& ptr) const override { return {}; }
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override
|
||||
{
|
||||
std::string model = getClassModel<Record>(ptr);
|
||||
std::string_view model = getClassModel<Record>(ptr);
|
||||
|
||||
// Hide meshes meshes/marker/* and *LOD.nif in ESM4 cells. It is a temporarty hack.
|
||||
// Needed because otherwise LOD meshes are rendered on top of normal meshes.
|
||||
// TODO: Figure out a better way find markers and LOD meshes; show LOD only outside of active grid.
|
||||
if (model.empty() || Misc::StringUtils::ciStartsWith(model, "meshes\\marker")
|
||||
if (model.empty() || Misc::StringUtils::ciStartsWith(model, "marker")
|
||||
|| Misc::StringUtils::ciEndsWith(model, "lod.nif"))
|
||||
return {};
|
||||
|
||||
|
|
|
@ -175,17 +175,14 @@ namespace MWClass
|
|||
return getCustomData(ptr).mIsFemale;
|
||||
}
|
||||
|
||||
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const ESM4NpcCustomData& data = getCustomData(ptr);
|
||||
if (data.mTraits == nullptr)
|
||||
return {};
|
||||
std::string_view model;
|
||||
if (data.mTraits->mIsTES4)
|
||||
model = data.mTraits->mModel;
|
||||
else
|
||||
model = data.mIsFemale ? data.mRace->mModelFemale : data.mRace->mModelMale;
|
||||
return Misc::ResourceHelpers::correctMeshPath(model);
|
||||
return data.mTraits->mModel;
|
||||
return data.mIsFemale ? data.mRace->mModelFemale : data.mRace->mModelMale;
|
||||
}
|
||||
|
||||
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
|||
return ESM4Impl::getToolTipInfo(getName(ptr), count);
|
||||
}
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
static const ESM4::Npc* getTraitsRecord(const MWWorld::Ptr& ptr);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Ingredient::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Ingredient::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Ingredient>(ptr);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace MWClass
|
|||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace MWClass
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string Light::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Light::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Light>(ptr);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace MWClass
|
|||
float getRemainingUsageTime(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Returns the remaining duration of the object.
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Lockpick::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Lockpick::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Lockpick>(ptr);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Miscellaneous::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Miscellaneous::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Miscellaneous>(ptr);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace MWClass
|
|||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#include <components/esm3/loadsoun.hpp>
|
||||
#include <components/esm3/npcstate.hpp>
|
||||
#include <components/settings/values.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -424,45 +426,51 @@ namespace MWClass
|
|||
return (ref->mBase->mRecordFlags & ESM::FLAG_Persistent) != 0;
|
||||
}
|
||||
|
||||
std::string Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
||||
std::string_view model = Settings::models().mBaseanim.get();
|
||||
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(ref->mBase->mRace);
|
||||
if (race->mData.mFlags & ESM::Race::Beast)
|
||||
model = Settings::models().mBaseanimkna.get();
|
||||
// Base animations should be in the meshes dir
|
||||
constexpr std::string_view prefix = "meshes/";
|
||||
assert(VFS::Path::pathEqual(prefix, model.substr(0, prefix.size())));
|
||||
return model.substr(prefix.size());
|
||||
}
|
||||
|
||||
std::string Npc::getCorrectedModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
||||
|
||||
std::string model = Settings::models().mBaseanim;
|
||||
const std::string& model = Settings::models().mBaseanim;
|
||||
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(ref->mBase->mRace);
|
||||
if (race->mData.mFlags & ESM::Race::Beast)
|
||||
model = Settings::models().mBaseanimkna;
|
||||
return Settings::models().mBaseanimkna;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
||||
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
||||
const auto& esmStore = MWBase::Environment::get().getESMStore();
|
||||
const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace);
|
||||
if (race && race->mData.mFlags & ESM::Race::Beast)
|
||||
models.push_back(Settings::models().mBaseanimkna);
|
||||
|
||||
// keep these always loaded just in case
|
||||
models.push_back(Settings::models().mXargonianswimkna);
|
||||
models.push_back(Settings::models().mXbaseanimfemale);
|
||||
models.push_back(Settings::models().mXbaseanim);
|
||||
models.push_back(getModel(ptr));
|
||||
|
||||
if (!npc->mBase->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(npc->mBase->mModel));
|
||||
models.push_back(npc->mBase->mModel);
|
||||
|
||||
if (!npc->mBase->mHead.empty())
|
||||
{
|
||||
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||
if (head)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
||||
models.push_back(head->mModel);
|
||||
}
|
||||
if (!npc->mBase->mHair.empty())
|
||||
{
|
||||
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||
if (hair)
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
||||
models.push_back(hair->mModel);
|
||||
}
|
||||
|
||||
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
||||
|
@ -486,7 +494,7 @@ namespace MWClass
|
|||
|
||||
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
||||
if (part && !part->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
models.push_back(part->mModel);
|
||||
}
|
||||
};
|
||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||
|
@ -501,7 +509,7 @@ namespace MWClass
|
|||
}
|
||||
else
|
||||
{
|
||||
std::string model = equipped->getClass().getModel(*equipped);
|
||||
std::string_view model = equipped->getClass().getModel(*equipped);
|
||||
if (!model.empty())
|
||||
models.push_back(model);
|
||||
}
|
||||
|
@ -510,14 +518,14 @@ namespace MWClass
|
|||
}
|
||||
|
||||
// preload body parts
|
||||
if (race)
|
||||
if (const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace))
|
||||
{
|
||||
const std::vector<const ESM::BodyPart*>& parts
|
||||
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
||||
for (const ESM::BodyPart* part : parts)
|
||||
{
|
||||
if (part && !part->mModel.empty())
|
||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
||||
models.push_back(part->mModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -655,7 +663,7 @@ namespace MWClass
|
|||
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
||||
if (!weapon.isEmpty())
|
||||
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
||||
skillUsageSucceeded(ptr, weapskill, 0);
|
||||
skillUsageSucceeded(ptr, weapskill, ESM::Skill::Weapon_SuccessfulHit);
|
||||
|
||||
const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence();
|
||||
|
||||
|
@ -845,7 +853,7 @@ namespace MWClass
|
|||
|
||||
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, skill, 0);
|
||||
skillUsageSucceeded(ptr, skill, ESM::Skill::Armor_HitByOpponent);
|
||||
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||
|
@ -855,7 +863,7 @@ namespace MWClass
|
|||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||
}
|
||||
else if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
|
||||
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, ESM::Skill::Armor_HitByOpponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -917,35 +925,38 @@ namespace MWClass
|
|||
}
|
||||
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
|
||||
const bool isPursuing = aiSequence.isInPursuit() && actor == MWMechanics::getPlayer();
|
||||
const bool inCombatWithActor = aiSequence.isInCombat(actor) || isPursuing;
|
||||
|
||||
if (stats.isDead())
|
||||
{
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation && !stats.getAiSequence().isInCombat())
|
||||
// by default user can loot non-fighting actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// otherwise wait until death animation
|
||||
if (stats.isDeathAnimationFinished())
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
}
|
||||
else if (!stats.getAiSequence().isInCombat())
|
||||
else
|
||||
{
|
||||
if (stats.getKnockedDown() || MWBase::Environment::get().getMechanicsManager()->isSneaking(actor))
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr); // stealing
|
||||
const bool allowStealingFromKO
|
||||
= Settings::game().mAlwaysAllowStealingFromKnockedOutActors || !inCombatWithActor;
|
||||
if (stats.getKnockedDown() && allowStealingFromKO)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// Can't talk to werewolves
|
||||
if (!getNpcStats(ptr).isWerewolf())
|
||||
const bool allowStealingWhileSneaking = !inCombatWithActor;
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isSneaking(actor) && allowStealingWhileSneaking)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
const bool allowTalking = !inCombatWithActor && !getNpcStats(ptr).isWerewolf();
|
||||
if (allowTalking)
|
||||
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
||||
}
|
||||
else // In combat
|
||||
{
|
||||
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && stats.getKnockedDown())
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr); // stealing
|
||||
}
|
||||
|
||||
// Tribunal and some mod companions oddly enough must use open action as fallback
|
||||
if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
if (inCombatWithActor)
|
||||
return std::make_unique<MWWorld::FailedAction>("#{sActorInCombat}");
|
||||
|
||||
return std::make_unique<MWWorld::FailedAction>();
|
||||
}
|
||||
|
@ -1079,7 +1090,8 @@ namespace MWClass
|
|||
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
if (!customData.mNpcStats.getAiSequence().isInCombat())
|
||||
const MWMechanics::AiSequence& aiSeq = customData.mNpcStats.getAiSequence();
|
||||
if (!aiSeq.isInCombat() || aiSeq.isFleeing())
|
||||
return true;
|
||||
|
||||
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && customData.mNpcStats.getKnockedDown())
|
||||
|
@ -1131,16 +1143,7 @@ namespace MWClass
|
|||
|
||||
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
||||
{
|
||||
MWMechanics::NpcStats& stats = getNpcStats(ptr);
|
||||
|
||||
if (stats.isWerewolf())
|
||||
return;
|
||||
|
||||
MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
||||
|
||||
const ESM::Class* class_ = MWBase::Environment::get().getESMStore()->get<ESM::Class>().find(ref->mBase->mClass);
|
||||
|
||||
stats.useSkill(skill, *class_, usageType, extraFactor);
|
||||
MWBase::Environment::get().getLuaManager()->skillUse(ptr, skill, usageType, extraFactor);
|
||||
}
|
||||
|
||||
float Npc::getArmorRating(const MWWorld::Ptr& ptr) const
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace MWClass
|
|||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||
const MWMechanics::DamageSourceType sourceType) const override;
|
||||
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||
///< list getModel().
|
||||
|
||||
|
@ -131,7 +131,9 @@ namespace MWClass
|
|||
|
||||
ESM::RefId getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const override;
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
std::string getCorrectedModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Potion::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Potion::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Potion>(ptr);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace MWClass
|
|||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Probe::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Probe::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Probe>(ptr);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Repair::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Repair::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Repair>(ptr);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace MWClass
|
|||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< Return name of inventory icon.
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu (default implementation: return a
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace MWClass
|
|||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||
}
|
||||
|
||||
std::string Static::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Static::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Static>(ptr);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MWClass
|
|||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
std::string Weapon::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
std::string_view Weapon::getModel(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
return getClassModel<ESM::Weapon>(ptr);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace MWClass
|
|||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||
///< Generate action for using via inventory menu
|
||||
|
||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||
|
||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||
|
||||
|
|
|
@ -543,7 +543,8 @@ namespace MWDialogue
|
|||
mPermanentDispositionChange += perm;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1);
|
||||
player.getClass().skillUsageSucceeded(
|
||||
player, ESM::Skill::Speechcraft, success ? ESM::Skill::Speechcraft_Success : ESM::Skill::Speechcraft_Fail);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
|
|
@ -375,12 +375,18 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
|
|||
return mChoice;
|
||||
|
||||
case SelectWrapper::Function_AiSetting:
|
||||
{
|
||||
int argument = select.getArgument();
|
||||
if (argument < 0 || argument > 3)
|
||||
{
|
||||
throw std::runtime_error("AiSetting index is out of range");
|
||||
}
|
||||
|
||||
return mActor.getClass()
|
||||
.getCreatureStats(mActor)
|
||||
.getAiSetting((MWMechanics::AiSetting)select.getArgument())
|
||||
.getAiSetting(static_cast<MWMechanics::AiSetting>(argument))
|
||||
.getModified(false);
|
||||
|
||||
}
|
||||
case SelectWrapper::Function_PcAttribute:
|
||||
{
|
||||
ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());
|
||||
|
|
|
@ -126,7 +126,7 @@ namespace
|
|||
MWGui::BookPage::ClickCallback callback = [this](intptr_t linkId) { notifyTopicClicked(linkId); };
|
||||
|
||||
getPage(LeftBookPage)->adviseLinkClicked(callback);
|
||||
getPage(RightBookPage)->adviseLinkClicked(callback);
|
||||
getPage(RightBookPage)->adviseLinkClicked(std::move(callback));
|
||||
|
||||
getPage(LeftBookPage)->eventMouseWheel
|
||||
+= MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
|
||||
|
@ -140,7 +140,7 @@ namespace
|
|||
|
||||
getPage(LeftTopicIndex)->adviseLinkClicked(callback);
|
||||
getPage(CenterTopicIndex)->adviseLinkClicked(callback);
|
||||
getPage(RightTopicIndex)->adviseLinkClicked(callback);
|
||||
getPage(RightTopicIndex)->adviseLinkClicked(std::move(callback));
|
||||
}
|
||||
|
||||
adjustButton(PrevPageBTN);
|
||||
|
@ -376,7 +376,7 @@ namespace
|
|||
setVisible(PageTwoNum, relPages > 1);
|
||||
|
||||
getPage(LeftBookPage)->showPage((relPages > 0) ? book : Book(), page + 0);
|
||||
getPage(RightBookPage)->showPage((relPages > 0) ? book : Book(), page + 1);
|
||||
getPage(RightBookPage)->showPage((relPages > 0) ? std::move(book) : Book(), page + 1);
|
||||
|
||||
setText(PageOneNum, page + 1);
|
||||
setText(PageTwoNum, page + 2);
|
||||
|
|
|
@ -164,8 +164,10 @@ namespace MWGui
|
|||
const MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
||||
|
||||
setClassImage(mClassImage,
|
||||
ESM::RefId::stringRefId(getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(0),
|
||||
pcStats.getSkillIncreasesForSpecialization(1), pcStats.getSkillIncreasesForSpecialization(2))));
|
||||
ESM::RefId::stringRefId(
|
||||
getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Combat),
|
||||
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Magic),
|
||||
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Stealth))));
|
||||
|
||||
int level = creatureStats.getLevel() + 1;
|
||||
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MWGui
|
|||
return false;
|
||||
}
|
||||
else
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1);
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, ESM::Skill::Sneak_PickPocket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <MyGUI_TextIterator.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -174,10 +175,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
// increase skill
|
||||
MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
|
||||
|
||||
const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
|
||||
pcStats.increaseSkill(skill->mId, *class_, true);
|
||||
MWBase::Environment::get().getLuaManager()->skillLevelUp(player, skill->mId, "trainer");
|
||||
|
||||
// remove gold
|
||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
|
||||
|
|
|
@ -371,7 +371,7 @@ namespace MWGui::Widgets
|
|||
|
||||
std::string spellLine = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);
|
||||
|
||||
if (mEffectParams.mMagnMin || mEffectParams.mMagnMax)
|
||||
if ((mEffectParams.mMagnMin || mEffectParams.mMagnMax) && !mEffectParams.mNoMagnitude)
|
||||
{
|
||||
ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType();
|
||||
if (displayType == ESM::MagicEffect::MDT_TimesInt)
|
||||
|
@ -386,7 +386,7 @@ namespace MWGui::Widgets
|
|||
|
||||
spellLine += formatter.str();
|
||||
}
|
||||
else if (displayType != ESM::MagicEffect::MDT_None && !mEffectParams.mNoMagnitude)
|
||||
else if (displayType != ESM::MagicEffect::MDT_None)
|
||||
{
|
||||
spellLine += " " + MyGUI::utility::toString(mEffectParams.mMagnMin);
|
||||
if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include <components/l10n/manager.hpp>
|
||||
|
||||
#include <components/lua_ui/util.hpp>
|
||||
#include <components/lua_ui/widget.hpp>
|
||||
|
||||
#include <components/settings/values.hpp>
|
||||
|
||||
|
@ -546,7 +547,8 @@ namespace MWGui
|
|||
{
|
||||
try
|
||||
{
|
||||
LuaUi::clearUserInterface();
|
||||
LuaUi::clearGameInterface();
|
||||
LuaUi::clearMenuInterface();
|
||||
|
||||
mStatsWatcher.reset();
|
||||
|
||||
|
@ -1675,7 +1677,10 @@ namespace MWGui
|
|||
|
||||
void WindowManager::onKeyFocusChanged(MyGUI::Widget* widget)
|
||||
{
|
||||
if (widget && widget->castType<MyGUI::EditBox>(false))
|
||||
bool isEditBox = widget && widget->castType<MyGUI::EditBox>(false);
|
||||
LuaUi::WidgetExtension* luaWidget = dynamic_cast<LuaUi::WidgetExtension*>(widget);
|
||||
bool capturesInput = luaWidget ? luaWidget->isTextInput() : isEditBox;
|
||||
if (widget && capturesInput)
|
||||
SDL_StartTextInput();
|
||||
else
|
||||
SDL_StopTextInput();
|
||||
|
@ -2173,11 +2178,16 @@ namespace MWGui
|
|||
mConsole->print(msg, color);
|
||||
}
|
||||
|
||||
void WindowManager::setConsoleMode(const std::string& mode)
|
||||
void WindowManager::setConsoleMode(std::string_view mode)
|
||||
{
|
||||
mConsole->setConsoleMode(mode);
|
||||
}
|
||||
|
||||
const std::string& WindowManager::getConsoleMode()
|
||||
{
|
||||
return mConsole->getConsoleMode();
|
||||
}
|
||||
|
||||
void WindowManager::createCursors()
|
||||
{
|
||||
MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
|
||||
|
|
|
@ -192,7 +192,8 @@ namespace MWGui
|
|||
void setConsoleSelectedObject(const MWWorld::Ptr& object) override;
|
||||
MWWorld::Ptr getConsoleSelectedObject() const override;
|
||||
void printToConsole(const std::string& msg, std::string_view color) override;
|
||||
void setConsoleMode(const std::string& mode) override;
|
||||
void setConsoleMode(std::string_view mode) override;
|
||||
const std::string& getConsoleMode() override;
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time time left to start drowning
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
|
@ -119,15 +120,22 @@ namespace MWInput
|
|||
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
||||
mBindingsManager->mouseReleased(arg, id);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||
{ MWBase::LuaManager::InputEvent::MouseButtonReleased, arg.button });
|
||||
}
|
||||
|
||||
void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent& arg)
|
||||
{
|
||||
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
||||
if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())
|
||||
{
|
||||
mBindingsManager->mouseWheelMoved(arg);
|
||||
}
|
||||
|
||||
input->setJoystickLastUsed(false);
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent({ MWBase::LuaManager::InputEvent::MouseWheel,
|
||||
MWBase::LuaManager::InputEvent::WheelChange{ arg.x, arg.y } });
|
||||
}
|
||||
|
||||
void MouseManager::mousePressed(const SDL_MouseButtonEvent& arg, Uint8 id)
|
||||
|
@ -161,7 +169,11 @@ namespace MWInput
|
|||
const MWGui::SettingsWindow* settingsWindow
|
||||
= MWBase::Environment::get().getWindowManager()->getSettingsWindow();
|
||||
if ((!settingsWindow || !settingsWindow->isVisible()) && !input->controlsDisabled())
|
||||
{
|
||||
mBindingsManager->mousePressed(arg, id);
|
||||
}
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||
{ MWBase::LuaManager::InputEvent::MouseButtonPressed, arg.button });
|
||||
}
|
||||
|
||||
void MouseManager::updateCursorMode()
|
||||
|
|
|
@ -21,24 +21,6 @@
|
|||
#include "animationbindings.hpp"
|
||||
#include <array>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
struct AnimationGroup;
|
||||
struct TextKeyCallback;
|
||||
}
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWLua::AnimationGroup> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<std::shared_ptr<MWLua::TextKeyCallback>> : std::false_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
using BlendMask = MWRender::Animation::BlendMask;
|
||||
|
@ -119,7 +101,7 @@ namespace MWLua
|
|||
if (asTable)
|
||||
{
|
||||
AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default);
|
||||
for (auto entry : asTable.value())
|
||||
for (const auto& entry : asTable.value())
|
||||
{
|
||||
if (!entry.first.is<BoneGroup>() || !entry.second.is<Priority>())
|
||||
throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only");
|
||||
|
@ -249,7 +231,7 @@ namespace MWLua
|
|||
// Extended variant of MWScript's PlayGroup and LoopGroup
|
||||
api["playQueued"] = sol::overload(
|
||||
[mechanics](const sol::object& object, const std::string& groupname, const sol::table& options) {
|
||||
int numberOfLoops = options.get_or("loops", std::numeric_limits<int>::max());
|
||||
uint32_t numberOfLoops = options.get_or("loops", std::numeric_limits<uint32_t>::max());
|
||||
float speed = options.get_or("speed", 1.f);
|
||||
std::string startKey = options.get_or<std::string>("startkey", "start");
|
||||
std::string stopKey = options.get_or<std::string>("stopkey", "stop");
|
||||
|
@ -265,7 +247,7 @@ namespace MWLua
|
|||
});
|
||||
|
||||
api["playBlended"] = [](const sol::object& object, std::string_view groupname, const sol::table& options) {
|
||||
int loops = options.get_or("loops", 0);
|
||||
uint32_t loops = options.get_or("loops", 0u);
|
||||
MWRender::Animation::AnimPriority priority = getPriorityArgument(options);
|
||||
BlendMask blendMask = options.get_or("blendmask", BlendMask::BlendMask_All);
|
||||
bool autoDisable = options.get_or("autodisable", true);
|
||||
|
@ -344,18 +326,19 @@ namespace MWLua
|
|||
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) {
|
||||
auto model = getStaticModelOrThrow(staticOrID);
|
||||
context.mLuaManager->addAction(
|
||||
[world, model, worldPos]() { world->spawnEffect(model, "", worldPos); }, "openmw.vfx.spawn");
|
||||
[world, model = std::move(model), worldPos]() { world->spawnEffect(model, "", worldPos); },
|
||||
"openmw.vfx.spawn");
|
||||
},
|
||||
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) {
|
||||
auto model = getStaticModelOrThrow(staticOrID);
|
||||
|
||||
bool magicVfx = options.get_or("mwMagicVfx", true);
|
||||
std::string textureOverride = options.get_or<std::string>("particleTextureOverride", "");
|
||||
std::string texture = options.get_or<std::string>("particleTextureOverride", "");
|
||||
float scale = options.get_or("scale", 1.f);
|
||||
|
||||
context.mLuaManager->addAction(
|
||||
[world, model, textureOverride, worldPos, scale, magicVfx]() {
|
||||
world->spawnEffect(model, textureOverride, worldPos, scale, magicVfx);
|
||||
[world, model = std::move(model), texture = std::move(texture), worldPos, scale, magicVfx]() {
|
||||
world->spawnEffect(model, texture, worldPos, scale, magicVfx);
|
||||
},
|
||||
"openmw.vfx.spawn");
|
||||
});
|
||||
|
|
55
apps/openmw/mwlua/birthsignbindings.cpp
Normal file
55
apps/openmw/mwlua/birthsignbindings.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include <components/esm3/loadbsgn.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "birthsignbindings.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
#include "types/types.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<ESM::BirthSign> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<MWWorld::Store<ESM::BirthSign>> : std::false_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initBirthSignRecordBindings(const Context& context)
|
||||
{
|
||||
sol::state_view& lua = context.mLua->sol();
|
||||
sol::table birthSigns(context.mLua->sol(), sol::create);
|
||||
addRecordFunctionBinding<ESM::BirthSign>(birthSigns, context);
|
||||
|
||||
auto signT = lua.new_usertype<ESM::BirthSign>("ESM3_BirthSign");
|
||||
signT[sol::meta_function::to_string] = [](const ESM::BirthSign& rec) -> std::string {
|
||||
return "ESM3_BirthSign[" + rec.mId.toDebugString() + "]";
|
||||
};
|
||||
signT["id"] = sol::readonly_property([](const ESM::BirthSign& rec) { return rec.mId.serializeText(); });
|
||||
signT["name"] = sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mName; });
|
||||
signT["description"]
|
||||
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
|
||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs);
|
||||
});
|
||||
signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table {
|
||||
sol::table res(lua, sol::create);
|
||||
for (size_t i = 0; i < rec.mPowers.mList.size(); ++i)
|
||||
res[i + 1] = rec.mPowers.mList[i].serializeText();
|
||||
return res;
|
||||
});
|
||||
|
||||
return LuaUtil::makeReadOnly(birthSigns);
|
||||
}
|
||||
}
|
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef MWLUA_BIRTHSIGNBINDINGS_H
|
||||
#define MWLUA_BIRTHSIGNBINDINGS_H
|
||||
|
||||
#include <sol/forward.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initBirthSignRecordBindings(const Context& context);
|
||||
}
|
||||
|
||||
#endif // MWLUA_BIRTHSIGNBINDINGS_H
|
|
@ -25,7 +25,7 @@ namespace sol
|
|||
namespace MWLua
|
||||
{
|
||||
|
||||
sol::table initCoreClassBindings(const Context& context)
|
||||
sol::table initClassRecordBindings(const Context& context)
|
||||
{
|
||||
sol::state_view& lua = context.mLua->sol();
|
||||
sol::table classes(context.mLua->sol(), sol::create);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initCoreClassBindings(const Context& context);
|
||||
sol::table initClassRecordBindings(const Context& context);
|
||||
}
|
||||
|
||||
#endif // MWLUA_CLASSBINDINGS_H
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace MWLua
|
|||
|
||||
struct Context
|
||||
{
|
||||
bool mIsMenu;
|
||||
bool mIsGlobal;
|
||||
LuaManager* mLuaManager;
|
||||
LuaUtil::LuaState* mLua;
|
||||
|
|
149
apps/openmw/mwlua/corebindings.cpp
Normal file
149
apps/openmw/mwlua/corebindings.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include "corebindings.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/esm3/loadfact.hpp>
|
||||
#include <components/lua/l10n.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/serialization.hpp>
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/datetimemanager.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "animationbindings.hpp"
|
||||
#include "factionbindings.hpp"
|
||||
#include "luaevents.hpp"
|
||||
#include "magicbindings.hpp"
|
||||
#include "soundbindings.hpp"
|
||||
#include "stats.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
static sol::table initContentFilesBindings(sol::state_view& lua)
|
||||
{
|
||||
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||
sol::table list(lua, sol::create);
|
||||
for (size_t i = 0; i < contentList.size(); ++i)
|
||||
list[i + 1] = Misc::StringUtils::lowerCase(contentList[i]);
|
||||
sol::table res(lua, sol::create);
|
||||
res["list"] = LuaUtil::makeReadOnly(list);
|
||||
res["indexOf"] = [&contentList](std::string_view contentFile) -> sol::optional<int> {
|
||||
for (size_t i = 0; i < contentList.size(); ++i)
|
||||
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||
return i + 1;
|
||||
return sol::nullopt;
|
||||
};
|
||||
res["has"] = [&contentList](std::string_view contentFile) -> bool {
|
||||
for (size_t i = 0; i < contentList.size(); ++i)
|
||||
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
return LuaUtil::makeReadOnly(res);
|
||||
}
|
||||
|
||||
void addCoreTimeBindings(sol::table& api, const Context& context)
|
||||
{
|
||||
MWWorld::DateTimeManager* timeManager = MWBase::Environment::get().getWorld()->getTimeManager();
|
||||
|
||||
api["getSimulationTime"] = [timeManager]() { return timeManager->getSimulationTime(); };
|
||||
api["getSimulationTimeScale"] = [timeManager]() { return timeManager->getSimulationTimeScale(); };
|
||||
api["getGameTime"] = [timeManager]() { return timeManager->getGameTime(); };
|
||||
api["getGameTimeScale"] = [timeManager]() { return timeManager->getGameTimeScale(); };
|
||||
api["isWorldPaused"] = [timeManager]() { return timeManager->isPaused(); };
|
||||
api["getRealTime"] = []() {
|
||||
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
};
|
||||
// TODO: remove in global context?
|
||||
api["getRealFrameDuration"] = []() { return MWBase::Environment::get().getFrameDuration(); };
|
||||
}
|
||||
|
||||
sol::table initCorePackage(const Context& context)
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
|
||||
if (lua->sol()["openmw_core"] != sol::nil)
|
||||
return lua->sol()["openmw_core"];
|
||||
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = Version::getLuaApiRevision(); // specified in CMakeLists.txt
|
||||
api["quit"] = [lua]() {
|
||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||
};
|
||||
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) {
|
||||
context.mLuaEvents->addGlobalEvent(
|
||||
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||
};
|
||||
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
||||
api["sound"] = initCoreSoundBindings(context);
|
||||
api["vfx"] = initCoreVfxBindings(context);
|
||||
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
||||
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||
for (size_t i = 0; i < contentList.size(); ++i)
|
||||
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||
return ESM::RefId(ESM::FormId{ index, int(i) }).serializeText();
|
||||
throw std::runtime_error("Content file not found: " + std::string(contentFile));
|
||||
};
|
||||
addCoreTimeBindings(api, context);
|
||||
api["magic"] = initCoreMagicBindings(context);
|
||||
api["stats"] = initCoreStatsBindings(context);
|
||||
|
||||
initCoreFactionBindings(context);
|
||||
api["factions"] = &MWBase::Environment::get().getESMStore()->get<ESM::Faction>();
|
||||
|
||||
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
|
||||
const MWWorld::Store<ESM::GameSetting>* gmstStore
|
||||
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||
api["getGMST"] = [lua = context.mLua, gmstStore](const std::string& setting) -> sol::object {
|
||||
const ESM::GameSetting* gmst = gmstStore->search(setting);
|
||||
if (gmst == nullptr)
|
||||
return sol::nil;
|
||||
const ESM::Variant& value = gmst->mValue;
|
||||
switch (value.getType())
|
||||
{
|
||||
case ESM::VT_Float:
|
||||
return sol::make_object<float>(lua->sol(), value.getFloat());
|
||||
case ESM::VT_Short:
|
||||
case ESM::VT_Long:
|
||||
case ESM::VT_Int:
|
||||
return sol::make_object<int>(lua->sol(), value.getInteger());
|
||||
case ESM::VT_String:
|
||||
return sol::make_object<std::string>(lua->sol(), value.getString());
|
||||
case ESM::VT_Unknown:
|
||||
case ESM::VT_None:
|
||||
break;
|
||||
}
|
||||
return sol::nil;
|
||||
};
|
||||
|
||||
lua->sol()["openmw_core"] = LuaUtil::makeReadOnly(api);
|
||||
return lua->sol()["openmw_core"];
|
||||
}
|
||||
|
||||
sol::table initCorePackageForMenuScripts(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
for (auto& [k, v] : LuaUtil::getMutableFromReadOnly(initCorePackage(context)))
|
||||
api[k] = v;
|
||||
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) {
|
||||
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
throw std::logic_error("Can't send global events when no game is loaded");
|
||||
}
|
||||
context.mLuaEvents->addGlobalEvent(
|
||||
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||
};
|
||||
api["sound"] = sol::nil;
|
||||
api["vfx"] = sol::nil;
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue