mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-13 00:13:17 +00:00
Merge branch 'master' of https://gitlab.com/OpenMW/openmw
This commit is contained in:
commit
b170500183
76 changed files with 895 additions and 380 deletions
146
CMakeLists.txt
146
CMakeLists.txt
|
|
@ -82,8 +82,8 @@ message(STATUS "Configuring OpenMW...")
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 50)
|
set(OPENMW_VERSION_MINOR 50)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 78)
|
set(OPENMW_LUA_API_REVISION 80)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
set(OPENMW_POSTPROCESSING_API_REVISION 3)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
set(OPENMW_VERSION_TAGHASH "")
|
set(OPENMW_VERSION_TAGHASH "")
|
||||||
|
|
@ -590,30 +590,10 @@ if(OPENMW_LTO_BUILD)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
set(OPENMW_CXX_FLAGS "-Wall -Wextra -Wundef -Wextra-semi -Wno-unused-parameter -pedantic -Wno-long-long -Wnon-virtual-dtor -Wunused ${OPENMW_CXX_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
||||||
|
endif()
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
|
||||||
set(OPENMW_CXX_FLAGS "-Wno-array-bounds ${OPENMW_CXX_FLAGS}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
|
||||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
|
||||||
endif ()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
|
||||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-unused-but-set-parameter -Wduplicated-branches -Wduplicated-cond -Wlogical-op")
|
|
||||||
endif()
|
|
||||||
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
|
|
||||||
# Extern
|
# Extern
|
||||||
|
|
||||||
|
|
@ -624,8 +604,42 @@ if (BUILD_OPENCS OR BUILD_OPENCS_TESTS)
|
||||||
add_subdirectory (extern/osgQt)
|
add_subdirectory (extern/osgQt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
|
add_compile_options("/W4")
|
||||||
|
|
||||||
|
set(WARNINGS_DISABLE
|
||||||
|
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||||
|
4127 # Conditional expression is constant
|
||||||
|
4996 # Function was declared deprecated
|
||||||
|
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(d ${WARNINGS_DISABLE})
|
||||||
|
add_compile_options("/wd${d}")
|
||||||
|
endforeach(d)
|
||||||
|
|
||||||
|
if(OPENMW_MSVC_WERROR)
|
||||||
|
add_compile_options("/WX")
|
||||||
|
endif()
|
||||||
|
else ()
|
||||||
|
add_compile_options("-Wall" "-Wextra" "-Wundef" "-Wextra-semi" "-Wno-unused-parameter" "-pedantic" "-Wno-long-long" "-Wnon-virtual-dtor" "-Wunused")
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||||
|
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
||||||
|
add_compile_options("-Wno-potentially-evaluated-expression")
|
||||||
|
endif ()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||||
|
add_compile_options("-Wno-unused-but-set-parameter" "-Wduplicated-branches" "-Wduplicated-cond" "-Wlogical-op")
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
||||||
|
add_compile_options("-Wno-array-bounds")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (OPENMW_CXX_FLAGS)
|
if (OPENMW_CXX_FLAGS)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}")
|
separate_arguments(OPENMW_CXX_FLAGS NATIVE_COMMAND "${OPENMW_CXX_FLAGS}")
|
||||||
|
add_compile_options(${OPENMW_CXX_FLAGS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
|
|
@ -715,87 +729,9 @@ if (WIN32)
|
||||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Play a bit with the warning levels
|
|
||||||
|
|
||||||
set(WARNINGS "/W4")
|
|
||||||
|
|
||||||
set(WARNINGS_DISABLE
|
|
||||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
|
||||||
4127 # Conditional expression is constant
|
|
||||||
4996 # Function was declared deprecated
|
|
||||||
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(d ${WARNINGS_DISABLE})
|
|
||||||
list(APPEND WARNINGS "/wd${d}")
|
|
||||||
endforeach(d)
|
|
||||||
|
|
||||||
if(OPENMW_MSVC_WERROR)
|
|
||||||
list(APPEND WARNINGS "/WX")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(components PRIVATE ${WARNINGS})
|
|
||||||
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
|
|
||||||
|
|
||||||
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
||||||
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_BSATOOL)
|
|
||||||
target_compile_options(bsatool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_ESMTOOL)
|
|
||||||
target_compile_options(esmtool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_ESSIMPORTER)
|
|
||||||
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_LAUNCHER)
|
|
||||||
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_MWINIIMPORTER)
|
|
||||||
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENCS)
|
|
||||||
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
|
||||||
target_compile_options(openmw PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_WIZARD)
|
|
||||||
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_COMPONENTS_TESTS)
|
|
||||||
target_compile_options(components-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_BENCHMARKS)
|
|
||||||
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_NAVMESHTOOL)
|
|
||||||
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_BULLETOBJECTTOOL)
|
|
||||||
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENCS_TESTS)
|
|
||||||
target_compile_options(openmw-cs-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENMW_TESTS)
|
|
||||||
target_compile_options(openmw-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ namespace
|
||||||
|
|
||||||
VFS::Manager vfs;
|
VFS::Manager vfs;
|
||||||
|
|
||||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||||
|
|
||||||
Settings::Manager::load(config);
|
Settings::Manager::load(config);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,8 @@ namespace
|
||||||
|
|
||||||
void compile(const std::string& name)
|
void compile(const std::string& name)
|
||||||
{
|
{
|
||||||
mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
|
mTechnique = std::make_unique<Technique>(
|
||||||
|
*mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true);
|
||||||
mTechnique->compile();
|
mTechnique->compile();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ namespace NavMeshTool
|
||||||
|
|
||||||
VFS::Manager vfs;
|
VFS::Manager vfs;
|
||||||
|
|
||||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||||
|
|
||||||
Settings::Manager::load(config);
|
Settings::Manager::load(config);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path)
|
||||||
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
if (isBSA(path))
|
if (isBSA(path))
|
||||||
return VFS::makeBsaArchive(path);
|
return VFS::makeBsaArchive(path, nullptr);
|
||||||
if (std::filesystem::is_directory(path))
|
if (std::filesystem::is_directory(path))
|
||||||
return std::make_unique<VFS::FileSystemArchive>(path);
|
return std::make_unique<VFS::FileSystemArchive>(path);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -198,7 +198,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
|
readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
||||||
, mArchives(archives)
|
, mArchives(archives)
|
||||||
, mVFS(std::make_unique<VFS::Manager>())
|
, mVFS(std::make_unique<VFS::Manager>())
|
||||||
{
|
{
|
||||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||||
|
|
||||||
mResourcesManager.setVFS(mVFS.get());
|
mResourcesManager.setVFS(mVFS.get());
|
||||||
|
|
||||||
|
|
@ -1465,7 +1465,7 @@ std::vector<ESM::RefId> CSMWorld::Data::getIds(bool listDeleted) const
|
||||||
void CSMWorld::Data::assetsChanged()
|
void CSMWorld::Data::assetsChanged()
|
||||||
{
|
{
|
||||||
mVFS.get()->reset();
|
mVFS.get()->reset();
|
||||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||||
|
|
||||||
const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics,
|
const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics,
|
||||||
UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos };
|
UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos };
|
||||||
|
|
|
||||||
|
|
@ -729,7 +729,7 @@ void OMW::Engine::prepareEngine()
|
||||||
|
|
||||||
mVFS = std::make_unique<VFS::Manager>();
|
mVFS = std::make_unique<VFS::Manager>();
|
||||||
|
|
||||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder());
|
||||||
|
|
||||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
||||||
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,7 @@ namespace MWBase
|
||||||
// Used in Lua bindings
|
// Used in Lua bindings
|
||||||
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
|
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
|
||||||
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;
|
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;
|
||||||
|
virtual bool isWindowVisible(std::string_view windowId) const = 0;
|
||||||
virtual std::vector<std::string_view> getAllWindowIds() const = 0;
|
virtual std::vector<std::string_view> getAllWindowIds() const = 0;
|
||||||
virtual std::vector<std::string_view> getAllowedWindowIds(MWGui::GuiMode mode) const = 0;
|
virtual std::vector<std::string_view> getAllowedWindowIds(MWGui::GuiMode mode) const = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -871,7 +871,8 @@ namespace MWClass
|
||||||
ptr.getRefData().setCustomData(nullptr);
|
ptr.getRefData().setCustomData(nullptr);
|
||||||
|
|
||||||
// Reset to original position
|
// Reset to original position
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
|
MWBase::Environment::get().getWorld()->moveObject(
|
||||||
|
ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3());
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(
|
MWBase::Environment::get().getWorld()->rotateObject(
|
||||||
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1427,7 +1427,8 @@ namespace MWClass
|
||||||
ptr.getRefData().setCustomData(nullptr);
|
ptr.getRefData().setCustomData(nullptr);
|
||||||
|
|
||||||
// Reset to original position
|
// Reset to original position
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
|
MWBase::Environment::get().getWorld()->moveObject(
|
||||||
|
ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3());
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(
|
MWBase::Environment::get().getWorld()->rotateObject(
|
||||||
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ namespace MWGui
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
|
, mUpdateNextFrame(false)
|
||||||
, mDragAndDrop(dragAndDrop)
|
, mDragAndDrop(dragAndDrop)
|
||||||
, mMessageBoxManager(manager)
|
, mMessageBoxManager(manager)
|
||||||
{
|
{
|
||||||
|
|
@ -170,12 +171,20 @@ namespace MWGui
|
||||||
mItemView->resetScrollBars();
|
mItemView->resetScrollBars();
|
||||||
|
|
||||||
setTitle(actor.getClass().getName(actor));
|
setTitle(actor.getClass().getName(actor));
|
||||||
|
|
||||||
|
mPtr.getClass().getContainerStore(mPtr).setContListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionWindow::onFrame(float dt)
|
void CompanionWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
checkReferenceAvailable();
|
checkReferenceAvailable();
|
||||||
updateEncumbranceBar();
|
|
||||||
|
if (mUpdateNextFrame)
|
||||||
|
{
|
||||||
|
updateEncumbranceBar();
|
||||||
|
mItemView->update();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionWindow::updateEncumbranceBar()
|
void CompanionWindow::updateEncumbranceBar()
|
||||||
|
|
@ -238,6 +247,16 @@ namespace MWGui
|
||||||
mSortModel = nullptr;
|
mSortModel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CompanionWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
|
bool CompanionWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
|
||||||
{
|
{
|
||||||
if (arg.button == SDL_CONTROLLER_BUTTON_A)
|
if (arg.button == SDL_CONTROLLER_BUTTON_A)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
#include "referenceinterface.hpp"
|
#include "referenceinterface.hpp"
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
namespace Widgets
|
namespace Widgets
|
||||||
|
|
@ -19,7 +21,7 @@ namespace MWGui
|
||||||
class SortFilterItemModel;
|
class SortFilterItemModel;
|
||||||
class CompanionItemModel;
|
class CompanionItemModel;
|
||||||
|
|
||||||
class CompanionWindow : public WindowBase, public ReferenceInterface
|
class CompanionWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);
|
CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);
|
||||||
|
|
@ -32,6 +34,9 @@ namespace MWGui
|
||||||
void onFrame(float dt) override;
|
void onFrame(float dt) override;
|
||||||
void clear() override { resetReference(); }
|
void clear() override { resetReference(); }
|
||||||
|
|
||||||
|
void itemAdded(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
void itemRemoved(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
|
||||||
std::string_view getWindowIdForLua() const override { return "Companion"; }
|
std::string_view getWindowIdForLua() const override { return "Companion"; }
|
||||||
|
|
||||||
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
|
bool onControllerButtonEvent(const SDL_ControllerButtonEvent& arg) override;
|
||||||
|
|
@ -45,6 +50,7 @@ namespace MWGui
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
CompanionItemModel* mModel;
|
CompanionItemModel* mModel;
|
||||||
int mSelectedItem;
|
int mSelectedItem;
|
||||||
|
bool mUpdateNextFrame;
|
||||||
|
|
||||||
DragAndDrop* mDragAndDrop;
|
DragAndDrop* mDragAndDrop;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ namespace MWGui
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
|
, mUpdateNextFrame(false)
|
||||||
, mTreatNextOpenAsLoot(false)
|
, mTreatNextOpenAsLoot(false)
|
||||||
{
|
{
|
||||||
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
||||||
|
|
@ -196,6 +197,8 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
|
||||||
|
|
||||||
setTitle(container.getClass().getName(container));
|
setTitle(container.getClass().getName(container));
|
||||||
|
|
||||||
|
mPtr.getClass().getContainerStore(mPtr).setContListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainerWindow::resetReference()
|
void ContainerWindow::resetReference()
|
||||||
|
|
@ -399,4 +402,25 @@ namespace MWGui
|
||||||
mItemView->setActiveControllerWindow(active);
|
mItemView->setActiveControllerWindow(active);
|
||||||
WindowBase::setActiveControllerWindow(active);
|
WindowBase::setActiveControllerWindow(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::onFrame(float dt)
|
||||||
|
{
|
||||||
|
checkReferenceAvailable();
|
||||||
|
|
||||||
|
if (mUpdateNextFrame)
|
||||||
|
{
|
||||||
|
mItemView->update();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "itemmodel.hpp"
|
#include "itemmodel.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
namespace MyGUI
|
namespace MyGUI
|
||||||
{
|
{
|
||||||
class Gui;
|
class Gui;
|
||||||
|
|
@ -21,7 +23,7 @@ namespace MWGui
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class ContainerWindow : public WindowBase, public ReferenceInterface
|
class ContainerWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ContainerWindow(DragAndDrop* dragAndDrop);
|
ContainerWindow(DragAndDrop* dragAndDrop);
|
||||||
|
|
@ -30,7 +32,7 @@ namespace MWGui
|
||||||
void onClose() override;
|
void onClose() override;
|
||||||
void clear() override { resetReference(); }
|
void clear() override { resetReference(); }
|
||||||
|
|
||||||
void onFrame(float dt) override { checkReferenceAvailable(); }
|
void onFrame(float dt) override;
|
||||||
|
|
||||||
void resetReference() override;
|
void resetReference() override;
|
||||||
|
|
||||||
|
|
@ -38,6 +40,9 @@ namespace MWGui
|
||||||
|
|
||||||
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; }
|
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; }
|
||||||
|
|
||||||
|
void itemAdded(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
void itemRemoved(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
|
||||||
std::string_view getWindowIdForLua() const override { return "Container"; }
|
std::string_view getWindowIdForLua() const override { return "Container"; }
|
||||||
|
|
||||||
ControllerButtonStr* getControllerButtons() override;
|
ControllerButtonStr* getControllerButtons() override;
|
||||||
|
|
@ -54,6 +59,7 @@ namespace MWGui
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
ItemModel* mModel;
|
ItemModel* mModel;
|
||||||
int mSelectedItem;
|
int mSelectedItem;
|
||||||
|
bool mUpdateNextFrame;
|
||||||
bool mTreatNextOpenAsLoot;
|
bool mTreatNextOpenAsLoot;
|
||||||
MyGUI::Button* mDisposeCorpseButton;
|
MyGUI::Button* mDisposeCorpseButton;
|
||||||
MyGUI::Button* mTakeButton;
|
MyGUI::Button* mTakeButton;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include "controllers.hpp"
|
#include "controllers.hpp"
|
||||||
#include "inventorywindow.hpp"
|
#include "inventorywindow.hpp"
|
||||||
#include "itemview.hpp"
|
#include "itemview.hpp"
|
||||||
#include "itemwidget.hpp"
|
|
||||||
#include "sortfilteritemmodel.hpp"
|
#include "sortfilteritemmodel.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
|
|
@ -72,25 +71,25 @@ namespace MWGui
|
||||||
mSourceSortModel->addDragItem(mItem.mBase, count);
|
mSourceSortModel->addDragItem(mItem.mBase, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemWidget* baseWidget = MyGUI::Gui::getInstance().createWidget<ItemWidget>(
|
mDraggedWidget = MyGUI::Gui::getInstance().createWidget<ItemWidget>(
|
||||||
"MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop");
|
"MW_ItemIcon", 0, 0, 42, 42, MyGUI::Align::Default, "DragAndDrop");
|
||||||
|
|
||||||
Controllers::ControllerFollowMouse* controller
|
Controllers::ControllerFollowMouse* controller
|
||||||
= MyGUI::ControllerManager::getInstance()
|
= MyGUI::ControllerManager::getInstance()
|
||||||
.createItem(Controllers::ControllerFollowMouse::getClassTypeName())
|
.createItem(Controllers::ControllerFollowMouse::getClassTypeName())
|
||||||
->castType<Controllers::ControllerFollowMouse>();
|
->castType<Controllers::ControllerFollowMouse>();
|
||||||
MyGUI::ControllerManager::getInstance().addItem(baseWidget, controller);
|
MyGUI::ControllerManager::getInstance().addItem(mDraggedWidget, controller);
|
||||||
|
|
||||||
mDraggedWidget = baseWidget;
|
mDraggedWidget->setItem(mItem.mBase);
|
||||||
baseWidget->setItem(mItem.mBase);
|
mDraggedWidget->setNeedMouseFocus(false);
|
||||||
baseWidget->setNeedMouseFocus(false);
|
mDraggedWidget->setCount(count);
|
||||||
baseWidget->setCount(count);
|
|
||||||
|
|
||||||
sourceView->update();
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->setDragDrop(true);
|
MWBase::Environment::get().getWindowManager()->setDragDrop(true);
|
||||||
|
|
||||||
mIsOnDragAndDrop = true;
|
mIsOnDragAndDrop = true;
|
||||||
|
|
||||||
|
// Update item view after completing drag-and-drop setup
|
||||||
|
mSourceView->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DragAndDrop::drop(ItemModel* targetModel, ItemView* targetView)
|
void DragAndDrop::drop(ItemModel* targetModel, ItemView* targetView)
|
||||||
|
|
@ -124,6 +123,22 @@ namespace MWGui
|
||||||
mSourceView->update();
|
mSourceView->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DragAndDrop::update()
|
||||||
|
{
|
||||||
|
if (mIsOnDragAndDrop)
|
||||||
|
{
|
||||||
|
int count = mItem.mBase.getCellRef().getCount();
|
||||||
|
if (count < mDraggedCount)
|
||||||
|
{
|
||||||
|
mItem.mCount = count;
|
||||||
|
mDraggedCount = count;
|
||||||
|
mDraggedWidget->setCount(mDraggedCount);
|
||||||
|
mSourceSortModel->clearDragItems();
|
||||||
|
mSourceSortModel->addDragItem(mItem.mBase, mDraggedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DragAndDrop::onFrame()
|
void DragAndDrop::onFrame()
|
||||||
{
|
{
|
||||||
if (mIsOnDragAndDrop && mItem.mBase.getCellRef().getCount() == 0)
|
if (mIsOnDragAndDrop && mItem.mBase.getCellRef().getCount() == 0)
|
||||||
|
|
@ -137,8 +152,12 @@ namespace MWGui
|
||||||
// since mSourceView doesn't get updated in drag()
|
// since mSourceView doesn't get updated in drag()
|
||||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
|
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
|
||||||
|
|
||||||
MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget);
|
if (mDraggedWidget)
|
||||||
mDraggedWidget = nullptr;
|
{
|
||||||
|
MyGUI::Gui::getInstance().destroyWidget(mDraggedWidget);
|
||||||
|
mDraggedWidget = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->setDragDrop(false);
|
MWBase::Environment::get().getWindowManager()->setDragDrop(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define OPENMW_MWGUI_DRAGANDDROP_H
|
#define OPENMW_MWGUI_DRAGANDDROP_H
|
||||||
|
|
||||||
#include "itemmodel.hpp"
|
#include "itemmodel.hpp"
|
||||||
|
#include "itemwidget.hpp"
|
||||||
|
|
||||||
namespace MyGUI
|
namespace MyGUI
|
||||||
{
|
{
|
||||||
|
|
@ -18,7 +19,7 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool mIsOnDragAndDrop;
|
bool mIsOnDragAndDrop;
|
||||||
MyGUI::Widget* mDraggedWidget;
|
ItemWidget* mDraggedWidget;
|
||||||
ItemModel* mSourceModel;
|
ItemModel* mSourceModel;
|
||||||
ItemView* mSourceView;
|
ItemView* mSourceView;
|
||||||
SortFilterItemModel* mSourceSortModel;
|
SortFilterItemModel* mSourceSortModel;
|
||||||
|
|
@ -30,6 +31,7 @@ namespace MWGui
|
||||||
void startDrag(
|
void startDrag(
|
||||||
int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
|
int index, SortFilterItemModel* sortModel, ItemModel* sourceModel, ItemView* sourceView, int count);
|
||||||
void drop(ItemModel* targetModel, ItemView* targetView);
|
void drop(ItemModel* targetModel, ItemView* targetView);
|
||||||
|
void update();
|
||||||
void onFrame();
|
void onFrame();
|
||||||
|
|
||||||
void finish();
|
void finish();
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ namespace MWGui
|
||||||
, mTrading(false)
|
, mTrading(false)
|
||||||
, mUpdateTimer(0.f)
|
, mUpdateTimer(0.f)
|
||||||
, mPendingControllerAction(ControllerAction::None)
|
, mPendingControllerAction(ControllerAction::None)
|
||||||
|
, mUpdateNextFrame(false)
|
||||||
{
|
{
|
||||||
mPreviewTexture
|
mPreviewTexture
|
||||||
= std::make_unique<osgMyGUI::OSGTexture>(mPreview->getTexture(), mPreview->getTextureStateSet());
|
= std::make_unique<osgMyGUI::OSGTexture>(mPreview->getTexture(), mPreview->getTextureStateSet());
|
||||||
|
|
@ -169,6 +170,8 @@ namespace MWGui
|
||||||
auto tradeModel = std::make_unique<TradeItemModel>(std::make_unique<InventoryItemModel>(mPtr), MWWorld::Ptr());
|
auto tradeModel = std::make_unique<TradeItemModel>(std::make_unique<InventoryItemModel>(mPtr), MWWorld::Ptr());
|
||||||
mTradeModel = tradeModel.get();
|
mTradeModel = tradeModel.get();
|
||||||
|
|
||||||
|
mPtr.getClass().getInventoryStore(mPtr).setContListener(this);
|
||||||
|
|
||||||
if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings
|
if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings
|
||||||
mSortModel->setSourceModel(std::move(tradeModel));
|
mSortModel->setSourceModel(std::move(tradeModel));
|
||||||
else
|
else
|
||||||
|
|
@ -773,22 +776,21 @@ namespace MWGui
|
||||||
|
|
||||||
void InventoryWindow::onFrame(float dt)
|
void InventoryWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
updateEncumbranceBar();
|
if (mUpdateNextFrame)
|
||||||
|
|
||||||
if (mPinned)
|
|
||||||
{
|
{
|
||||||
mUpdateTimer += dt;
|
if (mTrading)
|
||||||
if (0.1f < mUpdateTimer)
|
|
||||||
{
|
{
|
||||||
mUpdateTimer = 0;
|
mTradeModel->updateBorrowed();
|
||||||
|
MWBase::Environment::get().getWindowManager()->getTradeWindow()->mTradeModel->updateBorrowed();
|
||||||
// Update pinned inventory in-game
|
MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateItemView();
|
||||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateOffer();
|
||||||
{
|
|
||||||
mItemView->update();
|
|
||||||
notifyContentChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateEncumbranceBar();
|
||||||
|
mDragAndDrop->update();
|
||||||
|
mItemView->update();
|
||||||
|
notifyContentChanged();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -940,6 +942,16 @@ namespace MWGui
|
||||||
mPreview->rebuild();
|
mPreview->rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InventoryWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InventoryWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const
|
MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const
|
||||||
{
|
{
|
||||||
const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize();
|
const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "windowpinnablebase.hpp"
|
#include "windowpinnablebase.hpp"
|
||||||
|
|
||||||
#include "../mwrender/characterpreview.hpp"
|
#include "../mwrender/characterpreview.hpp"
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
|
|
@ -30,7 +31,7 @@ namespace MWGui
|
||||||
class DragAndDrop;
|
class DragAndDrop;
|
||||||
class ItemModel;
|
class ItemModel;
|
||||||
|
|
||||||
class InventoryWindow : public WindowPinnableBase
|
class InventoryWindow : public WindowPinnableBase, public MWWorld::ContainerStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem);
|
InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem);
|
||||||
|
|
@ -62,6 +63,9 @@ namespace MWGui
|
||||||
|
|
||||||
void setGuiMode(GuiMode mode);
|
void setGuiMode(GuiMode mode);
|
||||||
|
|
||||||
|
void itemAdded(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
void itemRemoved(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
|
||||||
/// Cycle to previous/next weapon
|
/// Cycle to previous/next weapon
|
||||||
void cycle(bool next);
|
void cycle(bool next);
|
||||||
|
|
||||||
|
|
@ -111,7 +115,7 @@ namespace MWGui
|
||||||
std::unique_ptr<MWRender::InventoryPreview> mPreview;
|
std::unique_ptr<MWRender::InventoryPreview> mPreview;
|
||||||
|
|
||||||
bool mTrading;
|
bool mTrading;
|
||||||
float mUpdateTimer;
|
bool mUpdateNextFrame;
|
||||||
|
|
||||||
void toggleMaximized();
|
void toggleMaximized();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,14 @@
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::shared_ptr<fx::Technique>& getTechnique(const MyGUI::ListBox& list, size_t selected)
|
||||||
|
{
|
||||||
|
return *list.getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch)
|
void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch)
|
||||||
{
|
{
|
||||||
if (MyGUI::InputManager::getInstance().isShiftPressed()
|
if (MyGUI::InputManager::getInstance().isShiftPressed()
|
||||||
|
|
@ -117,7 +125,7 @@ namespace MWGui
|
||||||
if (index >= sender->getItemCount())
|
if (index >= sender->getItemCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
updateConfigView(sender->getItemNameAt(index));
|
updateConfigView(getTechnique(*sender, index)->getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessorHud::toggleTechnique(bool enabled)
|
void PostProcessorHud::toggleTechnique(bool enabled)
|
||||||
|
|
@ -131,7 +139,7 @@ namespace MWGui
|
||||||
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
mOverrideHint = list->getItemNameAt(selected);
|
mOverrideHint = list->getItemNameAt(selected);
|
||||||
|
|
||||||
auto technique = *list->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
|
auto technique = getTechnique(*list, selected);
|
||||||
if (technique->getDynamic())
|
if (technique->getDynamic())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -167,7 +175,7 @@ namespace MWGui
|
||||||
|
|
||||||
if (static_cast<size_t>(index) != selected)
|
if (static_cast<size_t>(index) != selected)
|
||||||
{
|
{
|
||||||
auto technique = *mActiveList->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
|
auto technique = getTechnique(*mActiveList, selected);
|
||||||
if (technique->getDynamic() || technique->getInternal())
|
if (technique->getDynamic() || technique->getInternal())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -290,16 +298,16 @@ namespace MWGui
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected()));
|
updateConfigView(getTechnique(*mInactiveList, mInactiveList->getIndexSelected())->getFileName());
|
||||||
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
|
||||||
updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected()));
|
updateConfigView(getTechnique(*mActiveList, mActiveList->getIndexSelected())->getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessorHud::updateConfigView(const std::string& name)
|
void PostProcessorHud::updateConfigView(VFS::Path::NormalizedView path)
|
||||||
{
|
{
|
||||||
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
auto technique = processor->loadTechnique(name);
|
auto technique = processor->loadTechnique(path);
|
||||||
|
|
||||||
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
||||||
return;
|
return;
|
||||||
|
|
@ -423,22 +431,22 @@ namespace MWGui
|
||||||
|
|
||||||
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
|
||||||
|
|
||||||
std::vector<std::string> techniques;
|
std::vector<VFS::Path::NormalizedView> techniques;
|
||||||
for (const auto& [name, _] : processor->getTechniqueMap())
|
for (const auto& vfsPath : processor->getTechniqueFiles())
|
||||||
techniques.push_back(name);
|
techniques.emplace_back(vfsPath);
|
||||||
std::sort(techniques.begin(), techniques.end(), Misc::StringUtils::ciLess);
|
std::sort(techniques.begin(), techniques.end());
|
||||||
|
|
||||||
for (const std::string& name : techniques)
|
for (VFS::Path::NormalizedView path : techniques)
|
||||||
{
|
{
|
||||||
auto technique = processor->loadTechnique(name);
|
auto technique = processor->loadTechnique(path);
|
||||||
|
|
||||||
if (!technique->getHidden() && !processor->isTechniqueEnabled(technique))
|
if (!technique->getHidden() && !processor->isTechniqueEnabled(technique))
|
||||||
{
|
{
|
||||||
std::string lowerName = Utf8Stream::lowerCaseUtf8(name);
|
std::string lowerName = Utf8Stream::lowerCaseUtf8(technique->getName());
|
||||||
std::string lowerCaption = mFilter->getCaption();
|
std::string lowerCaption = mFilter->getCaption();
|
||||||
lowerCaption = Utf8Stream::lowerCaseUtf8(lowerCaption);
|
lowerCaption = Utf8Stream::lowerCaseUtf8(lowerCaption);
|
||||||
if (lowerName.find(lowerCaption) != std::string::npos)
|
if (lowerName.find(lowerCaption) != std::string::npos)
|
||||||
mInactiveList->addItem(name, technique);
|
mInactiveList->addItem(technique->getName(), technique);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <MyGUI_ListBox.h>
|
#include <MyGUI_ListBox.h>
|
||||||
|
|
||||||
#include <components/settings/shadermanager.hpp>
|
#include <components/settings/shadermanager.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
namespace MyGUI
|
namespace MyGUI
|
||||||
{
|
{
|
||||||
|
|
@ -48,7 +49,7 @@ namespace MWGui
|
||||||
|
|
||||||
void notifyFilterChanged(MyGUI::EditBox* sender);
|
void notifyFilterChanged(MyGUI::EditBox* sender);
|
||||||
|
|
||||||
void updateConfigView(const std::string& name);
|
void updateConfigView(VFS::Path::NormalizedView path);
|
||||||
|
|
||||||
void notifyResetButtonClicked(MyGUI::Widget* sender);
|
void notifyResetButtonClicked(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -894,7 +894,8 @@ namespace MWGui
|
||||||
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
|
widget->setUserString("ToolTipLayout", "BirthSignToolTip");
|
||||||
widget->setUserString(
|
widget->setUserString(
|
||||||
"ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs));
|
"ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs));
|
||||||
std::string text = sign->mName + "\n#{fontcolourhtml=normal}" + sign->mDescription;
|
widget->setUserString("Caption_BirthSignName", sign->mName);
|
||||||
|
widget->setUserString("Caption_BirthSignDescription", sign->mDescription);
|
||||||
|
|
||||||
std::vector<const ESM::Spell*> abilities, powers, spells;
|
std::vector<const ESM::Spell*> abilities, powers, spells;
|
||||||
|
|
||||||
|
|
@ -915,26 +916,22 @@ namespace MWGui
|
||||||
spells.push_back(spell);
|
spells.push_back(spell);
|
||||||
}
|
}
|
||||||
|
|
||||||
using Category = std::pair<const std::vector<const ESM::Spell*>&, std::string_view>;
|
using Category = std::tuple<const std::vector<const ESM::Spell*>&, std::string_view, std::string_view>;
|
||||||
for (const auto& [category, label] : std::initializer_list<Category>{
|
std::initializer_list<Category> categories{ { abilities, "#{sBirthsignmenu1}", "Abilities" },
|
||||||
{ abilities, "sBirthsignmenu1" }, { powers, "sPowers" }, { spells, "sBirthsignmenu2" } })
|
{ powers, "#{sPowers}", "Powers" }, { spells, "#{sBirthsignmenu2}", "Spells" } };
|
||||||
|
|
||||||
|
for (const auto& [category, label, widgetName] : categories)
|
||||||
{
|
{
|
||||||
bool addHeader = true;
|
std::string text;
|
||||||
for (const ESM::Spell* spell : category)
|
if (!category.empty())
|
||||||
{
|
{
|
||||||
if (addHeader)
|
text = std::string(label) + "\n#{fontcolourhtml=normal}";
|
||||||
{
|
for (const ESM::Spell* spell : category)
|
||||||
text += "\n\n#{fontcolourhtml=header}#{";
|
text += spell->mName + ' ';
|
||||||
text += label;
|
text.pop_back();
|
||||||
text += '}';
|
|
||||||
addHeader = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
text += "\n#{fontcolourhtml=normal}" + spell->mName;
|
|
||||||
}
|
}
|
||||||
|
widget->setUserString("Caption_BirthSign" + std::string(widgetName), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
widget->setUserString("Caption_BirthSignText", text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace)
|
void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace)
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,25 @@ namespace MWGui
|
||||||
encumbrance = std::max(0.f, encumbrance);
|
encumbrance = std::max(0.f, encumbrance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TradeItemModel::updateBorrowed()
|
||||||
|
{
|
||||||
|
auto update = [](std::vector<ItemStack>& list) {
|
||||||
|
for (auto it = list.begin(); it != list.end();)
|
||||||
|
{
|
||||||
|
size_t actualCount = it->mBase.getCellRef().getCount();
|
||||||
|
if (actualCount < it->mCount)
|
||||||
|
it->mCount = actualCount;
|
||||||
|
if (it->mCount == 0)
|
||||||
|
it = list.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
update(mBorrowedFromUs);
|
||||||
|
update(mBorrowedToUs);
|
||||||
|
}
|
||||||
|
|
||||||
void TradeItemModel::abort()
|
void TradeItemModel::abort()
|
||||||
{
|
{
|
||||||
mBorrowedFromUs.clear();
|
mBorrowedFromUs.clear();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ namespace MWGui
|
||||||
|
|
||||||
void returnItemBorrowedFromUs(ModelIndex itemIndex, ItemModel* source, size_t count);
|
void returnItemBorrowedFromUs(ModelIndex itemIndex, ItemModel* source, size_t count);
|
||||||
|
|
||||||
|
/// Update borrowed items in this model
|
||||||
|
void updateBorrowed();
|
||||||
|
|
||||||
/// Permanently transfers items that were borrowed to us from another model to this model
|
/// Permanently transfers items that were borrowed to us from another model to this model
|
||||||
void transferItems();
|
void transferItems();
|
||||||
/// Aborts trade
|
/// Aborts trade
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ namespace MWGui
|
||||||
, mItemToSell(-1)
|
, mItemToSell(-1)
|
||||||
, mCurrentBalance(0)
|
, mCurrentBalance(0)
|
||||||
, mCurrentMerchantOffer(0)
|
, mCurrentMerchantOffer(0)
|
||||||
|
, mUpdateNextFrame(false)
|
||||||
{
|
{
|
||||||
getWidget(mFilterAll, "AllButton");
|
getWidget(mFilterAll, "AllButton");
|
||||||
getWidget(mFilterWeapon, "WeaponButton");
|
getWidget(mFilterWeapon, "WeaponButton");
|
||||||
|
|
@ -230,11 +231,24 @@ namespace MWGui
|
||||||
// Cycle to the buy window if it's not active.
|
// Cycle to the buy window if it's not active.
|
||||||
if (Settings::gui().mControllerMenus && !mActiveControllerWindow)
|
if (Settings::gui().mControllerMenus && !mActiveControllerWindow)
|
||||||
MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true);
|
MWBase::Environment::get().getWindowManager()->cycleActiveControllerWindow(true);
|
||||||
|
|
||||||
|
for (const auto& source : itemSources)
|
||||||
|
source.getClass().getContainerStore(source).setContListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TradeWindow::onFrame(float dt)
|
void TradeWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
checkReferenceAvailable();
|
checkReferenceAvailable();
|
||||||
|
|
||||||
|
if (isVisible() && mUpdateNextFrame)
|
||||||
|
{
|
||||||
|
mTradeModel->updateBorrowed();
|
||||||
|
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel()->updateBorrowed();
|
||||||
|
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
|
||||||
|
mItemView->update();
|
||||||
|
updateOffer();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
|
void TradeWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
|
||||||
|
|
@ -680,6 +694,7 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
bool TradeWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
|
bool TradeWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
|
||||||
{
|
{
|
||||||
if (arg.button == SDL_CONTROLLER_BUTTON_A)
|
if (arg.button == SDL_CONTROLLER_BUTTON_A)
|
||||||
|
|
@ -764,4 +779,19 @@ namespace MWGui
|
||||||
mItemView->setActiveControllerWindow(active);
|
mItemView->setActiveControllerWindow(active);
|
||||||
WindowBase::setActiveControllerWindow(active);
|
WindowBase::setActiveControllerWindow(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TradeWindow::updateItemView()
|
||||||
|
{
|
||||||
|
mItemView->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TradeWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TradeWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
#include "referenceinterface.hpp"
|
#include "referenceinterface.hpp"
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
namespace Gui
|
namespace Gui
|
||||||
{
|
{
|
||||||
class NumericEditBox;
|
class NumericEditBox;
|
||||||
|
|
@ -20,7 +22,7 @@ namespace MWGui
|
||||||
class SortFilterItemModel;
|
class SortFilterItemModel;
|
||||||
class TradeItemModel;
|
class TradeItemModel;
|
||||||
|
|
||||||
class TradeWindow : public WindowBase, public ReferenceInterface
|
class TradeWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TradeWindow();
|
TradeWindow();
|
||||||
|
|
@ -31,17 +33,17 @@ namespace MWGui
|
||||||
void onFrame(float dt) override;
|
void onFrame(float dt) override;
|
||||||
void clear() override { resetReference(); }
|
void clear() override { resetReference(); }
|
||||||
|
|
||||||
void borrowItem(int index, size_t count);
|
|
||||||
void returnItem(int index, size_t count);
|
|
||||||
|
|
||||||
int getMerchantServices();
|
|
||||||
|
|
||||||
bool exit() override;
|
bool exit() override;
|
||||||
|
|
||||||
void resetReference() override;
|
void resetReference() override;
|
||||||
|
|
||||||
void onDeleteCustomData(const MWWorld::Ptr& ptr) override;
|
void onDeleteCustomData(const MWWorld::Ptr& ptr) override;
|
||||||
|
|
||||||
|
void updateItemView();
|
||||||
|
|
||||||
|
void itemAdded(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
void itemRemoved(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
|
||||||
typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone;
|
typedef MyGUI::delegates::MultiDelegate<> EventHandle_TradeDone;
|
||||||
EventHandle_TradeDone eventTradeDone;
|
EventHandle_TradeDone eventTradeDone;
|
||||||
|
|
||||||
|
|
@ -51,6 +53,8 @@ namespace MWGui
|
||||||
void setActiveControllerWindow(bool active) override;
|
void setActiveControllerWindow(bool active) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class InventoryWindow;
|
||||||
|
|
||||||
ItemView* mItemView;
|
ItemView* mItemView;
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
TradeItemModel* mTradeModel;
|
TradeItemModel* mTradeModel;
|
||||||
|
|
@ -84,6 +88,8 @@ namespace MWGui
|
||||||
int mCurrentBalance;
|
int mCurrentBalance;
|
||||||
int mCurrentMerchantOffer;
|
int mCurrentMerchantOffer;
|
||||||
|
|
||||||
|
bool mUpdateNextFrame;
|
||||||
|
|
||||||
void sellToNpc(
|
void sellToNpc(
|
||||||
const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance
|
const MWWorld::Ptr& item, int count, bool boughtItem); ///< only used for adjusting the gold balance
|
||||||
void buyFromNpc(
|
void buyFromNpc(
|
||||||
|
|
@ -94,6 +100,11 @@ namespace MWGui
|
||||||
void onItemSelected(int index);
|
void onItemSelected(int index);
|
||||||
void sellItem(MyGUI::Widget* sender, int count);
|
void sellItem(MyGUI::Widget* sender, int count);
|
||||||
|
|
||||||
|
void borrowItem(int index, size_t count);
|
||||||
|
void returnItem(int index, size_t count);
|
||||||
|
|
||||||
|
int getMerchantServices();
|
||||||
|
|
||||||
void onFilterChanged(MyGUI::Widget* _sender);
|
void onFilterChanged(MyGUI::Widget* _sender);
|
||||||
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
||||||
void onOfferButtonClicked(MyGUI::Widget* _sender);
|
void onOfferButtonClicked(MyGUI::Widget* _sender);
|
||||||
|
|
|
||||||
|
|
@ -2578,6 +2578,14 @@ namespace MWGui
|
||||||
updateVisible();
|
updateVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowManager::isWindowVisible(std::string_view windowId) const
|
||||||
|
{
|
||||||
|
auto it = mLuaIdToWindow.find(windowId);
|
||||||
|
if (it == mLuaIdToWindow.end())
|
||||||
|
throw std::logic_error("Invalid window name: " + std::string(windowId));
|
||||||
|
return it->second->isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string_view> WindowManager::getAllWindowIds() const
|
std::vector<std::string_view> WindowManager::getAllWindowIds() const
|
||||||
{
|
{
|
||||||
std::vector<std::string_view> res;
|
std::vector<std::string_view> res;
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,7 @@ namespace MWGui
|
||||||
// Used in Lua bindings
|
// Used in Lua bindings
|
||||||
const std::vector<GuiMode>& getGuiModeStack() const override { return mGuiModes; }
|
const std::vector<GuiMode>& getGuiModeStack() const override { return mGuiModes; }
|
||||||
void setDisabledByLua(std::string_view windowId, bool disabled) override;
|
void setDisabledByLua(std::string_view windowId, bool disabled) override;
|
||||||
|
bool isWindowVisible(std::string_view windowId) const override;
|
||||||
std::vector<std::string_view> getAllWindowIds() const override;
|
std::vector<std::string_view> getAllWindowIds() const override;
|
||||||
std::vector<std::string_view> getAllowedWindowIds(GuiMode mode) const override;
|
std::vector<std::string_view> getAllowedWindowIds(GuiMode mode) const override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,8 @@ namespace MWLua
|
||||||
luaManager->addAction(
|
luaManager->addAction(
|
||||||
[=, window = std::move(window)]() { windowManager->setDisabledByLua(window, disabled); });
|
[=, window = std::move(window)]() { windowManager->setDisabledByLua(window, disabled); });
|
||||||
};
|
};
|
||||||
|
api["_isWindowVisible"]
|
||||||
|
= [windowManager](std::string_view window) { return windowManager->isWindowVisible(window); };
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// api["_showMouseCursor"] = [](bool) {};
|
// api["_showMouseCursor"] = [](bool) {};
|
||||||
|
|
|
||||||
|
|
@ -2096,12 +2096,17 @@ namespace MWRender
|
||||||
if (Settings::game().mGraphicHerbalism && ptr.getRefData().getCustomData() != nullptr
|
if (Settings::game().mGraphicHerbalism && ptr.getRefData().getCustomData() != nullptr
|
||||||
&& ObjectAnimation::canBeHarvested())
|
&& ObjectAnimation::canBeHarvested())
|
||||||
{
|
{
|
||||||
const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
harvest(ptr);
|
||||||
if (!store.hasVisibleItems())
|
}
|
||||||
{
|
}
|
||||||
HarvestVisitor visitor;
|
|
||||||
mObjectRoot->accept(visitor);
|
void ObjectAnimation::harvest(const MWWorld::Ptr& ptr)
|
||||||
}
|
{
|
||||||
|
const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||||
|
if (!store.hasVisibleItems())
|
||||||
|
{
|
||||||
|
HarvestVisitor visitor;
|
||||||
|
mObjectRoot->accept(visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,7 @@ namespace MWRender
|
||||||
|
|
||||||
virtual void setAccurateAiming(bool enabled) {}
|
virtual void setAccurateAiming(bool enabled) {}
|
||||||
virtual bool canBeHarvested() const { return false; }
|
virtual bool canBeHarvested() const { return false; }
|
||||||
|
virtual void harvest(const MWWorld::Ptr& ptr) {}
|
||||||
|
|
||||||
virtual void removeFromScene();
|
virtual void removeFromScene();
|
||||||
|
|
||||||
|
|
@ -498,6 +499,7 @@ namespace MWRender
|
||||||
bool animated, bool allowLight);
|
bool animated, bool allowLight);
|
||||||
|
|
||||||
bool canBeHarvested() const override;
|
bool canBeHarvested() const override;
|
||||||
|
void harvest(const MWWorld::Ptr& ptr) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdateVfxCallback : public SceneUtil::NodeCallback<UpdateVfxCallback>
|
class UpdateVfxCallback : public SceneUtil::NodeCallback<UpdateVfxCallback>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <osg/Texture3D>
|
#include <osg/Texture3D>
|
||||||
|
|
||||||
#include <components/files/conversion.hpp>
|
#include <components/files/conversion.hpp>
|
||||||
|
#include <components/misc/pathhelpers.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/misc/strings/lower.hpp>
|
#include <components/misc/strings/lower.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
@ -250,14 +251,12 @@ namespace MWRender
|
||||||
|
|
||||||
void PostProcessor::populateTechniqueFiles()
|
void PostProcessor::populateTechniqueFiles()
|
||||||
{
|
{
|
||||||
for (const auto& name : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir))
|
for (const auto& path : mVFS->getRecursiveDirectoryIterator(fx::Technique::sSubdir))
|
||||||
{
|
{
|
||||||
std::filesystem::path path = Files::pathFromUnicodeString(name);
|
std::string_view fileExt = Misc::getFileExtension(path);
|
||||||
std::string fileExt = Misc::StringUtils::lowerCase(Files::pathToUnicodeString(path.extension()));
|
if (path.parent().parent().empty() && fileExt == fx::Technique::sExt)
|
||||||
if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt)
|
|
||||||
{
|
{
|
||||||
const auto absolutePath = mVFS->getAbsoluteFileName(path);
|
mTechniqueFiles.emplace(path);
|
||||||
mTechniqueFileMap[Files::pathToUnicodeString(absolutePath.stem())] = absolutePath;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,7 +350,7 @@ namespace MWRender
|
||||||
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto lastWriteTime = std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()]);
|
const auto lastWriteTime = mVFS->getLastModified(technique->getFileName());
|
||||||
const bool isDirty = technique->setLastModificationTime(lastWriteTime);
|
const bool isDirty = technique->setLastModificationTime(lastWriteTime);
|
||||||
|
|
||||||
if (!isDirty)
|
if (!isDirty)
|
||||||
|
|
@ -363,7 +362,7 @@ namespace MWRender
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
|
||||||
if (technique->compile())
|
if (technique->compile())
|
||||||
Log(Debug::Info) << "Reloaded technique : " << mTechniqueFileMap[technique->getName()];
|
Log(Debug::Info) << "Reloaded technique : " << technique->getFileName();
|
||||||
|
|
||||||
mReload = technique->isValid();
|
mReload = technique->isValid();
|
||||||
}
|
}
|
||||||
|
|
@ -655,6 +654,14 @@ namespace MWRender
|
||||||
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
|
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
|
||||||
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
|
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
|
||||||
|
|
||||||
|
if (subPass.mMipMap)
|
||||||
|
{
|
||||||
|
subPass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subPass.mRenderTexture->setNumMipmapLevels(0);
|
||||||
|
}
|
||||||
subPass.mRenderTexture->setTextureSize(w, h);
|
subPass.mRenderTexture->setTextureSize(w, h);
|
||||||
subPass.mRenderTexture->dirtyTextureObject();
|
subPass.mRenderTexture->dirtyTextureObject();
|
||||||
|
|
||||||
|
|
@ -749,28 +756,35 @@ namespace MWRender
|
||||||
return technique->isValid();
|
return technique->isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame)
|
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(std::string_view name, bool loadNextFrame)
|
||||||
|
{
|
||||||
|
VFS::Path::Normalized path = fx::Technique::makeFileName(name);
|
||||||
|
return loadTechnique(VFS::Path::NormalizedView(path), loadNextFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame)
|
||||||
{
|
{
|
||||||
for (const auto& technique : mTemplates)
|
for (const auto& technique : mTemplates)
|
||||||
if (Misc::StringUtils::ciEqual(technique->getName(), name))
|
if (technique->getFileName() == path)
|
||||||
return technique;
|
return technique;
|
||||||
|
|
||||||
for (const auto& technique : mQueuedTemplates)
|
for (const auto& technique : mQueuedTemplates)
|
||||||
if (Misc::StringUtils::ciEqual(technique->getName(), name))
|
if (technique->getFileName() == path)
|
||||||
return technique;
|
return technique;
|
||||||
|
|
||||||
std::string realName = name;
|
std::string name;
|
||||||
auto fileIter = mTechniqueFileMap.find(name);
|
if (mTechniqueFiles.contains(path))
|
||||||
if (fileIter != mTechniqueFileMap.end())
|
name = mVFS->getStem(path);
|
||||||
realName = fileIter->first;
|
else
|
||||||
|
name = path.stem();
|
||||||
|
|
||||||
auto technique = std::make_shared<fx::Technique>(*mVFS, *mRendering.getResourceSystem()->getImageManager(),
|
auto technique = std::make_shared<fx::Technique>(*mVFS, *mRendering.getResourceSystem()->getImageManager(),
|
||||||
std::move(realName), renderWidth(), renderHeight(), mUBO, mNormalsSupported);
|
path, std::move(name), renderWidth(), renderHeight(), mUBO, mNormalsSupported);
|
||||||
|
|
||||||
technique->compile();
|
technique->compile();
|
||||||
|
|
||||||
if (technique->getStatus() != fx::Technique::Status::File_Not_exists)
|
if (technique->getStatus() != fx::Technique::Status::File_Not_exists)
|
||||||
technique->setLastModificationTime(std::filesystem::last_write_time(fileIter->second));
|
technique->setLastModificationTime(mVFS->getLastModified(path));
|
||||||
|
|
||||||
if (loadNextFrame)
|
if (loadNextFrame)
|
||||||
{
|
{
|
||||||
|
|
@ -825,7 +839,11 @@ namespace MWRender
|
||||||
void PostProcessor::toggleMode()
|
void PostProcessor::toggleMode()
|
||||||
{
|
{
|
||||||
for (auto& technique : mTemplates)
|
for (auto& technique : mTemplates)
|
||||||
|
{
|
||||||
|
if (technique->getStatus() == fx::Technique::Status::File_Not_exists)
|
||||||
|
continue;
|
||||||
technique->compile();
|
technique->compile();
|
||||||
|
}
|
||||||
|
|
||||||
dirtyTechniques(true);
|
dirtyTechniques(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace MWRender
|
||||||
|
|
||||||
const TechniqueList& getTemplates() const { return mTemplates; }
|
const TechniqueList& getTemplates() const { return mTemplates; }
|
||||||
|
|
||||||
const auto& getTechniqueMap() const { return mTechniqueFileMap; }
|
const auto& getTechniqueFiles() const { return mTechniqueFiles; }
|
||||||
|
|
||||||
void resize();
|
void resize();
|
||||||
|
|
||||||
|
|
@ -176,7 +176,8 @@ namespace MWRender
|
||||||
|
|
||||||
void toggleMode();
|
void toggleMode();
|
||||||
|
|
||||||
std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool loadNextFrame = false);
|
std::shared_ptr<fx::Technique> loadTechnique(VFS::Path::NormalizedView path, bool loadNextFrame = false);
|
||||||
|
std::shared_ptr<fx::Technique> loadTechnique(std::string_view name, bool loadNextFrame = false);
|
||||||
|
|
||||||
TechniqueList getChain();
|
TechniqueList getChain();
|
||||||
|
|
||||||
|
|
@ -232,8 +233,7 @@ namespace MWRender
|
||||||
TechniqueList mQueuedTemplates;
|
TechniqueList mQueuedTemplates;
|
||||||
TechniqueList mInternalTechniques;
|
TechniqueList mInternalTechniques;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::filesystem::path, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>
|
std::unordered_set<VFS::Path::Normalized, VFS::Path::Hash, std::equal_to<>> mTechniqueFiles;
|
||||||
mTechniqueFileMap;
|
|
||||||
|
|
||||||
RenderingManager& mRendering;
|
RenderingManager& mRendering;
|
||||||
osgViewer::Viewer* mViewer;
|
osgViewer::Viewer* mViewer;
|
||||||
|
|
|
||||||
|
|
@ -565,6 +565,8 @@ namespace MWRender
|
||||||
else
|
else
|
||||||
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
|
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->setUpNormalsRTForStateSet(mWaterGeom->getOrCreateStateSet(), true);
|
||||||
|
|
||||||
updateVisible();
|
updateVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwrender/animation.hpp"
|
||||||
|
|
||||||
#include "class.hpp"
|
#include "class.hpp"
|
||||||
#include "containerstore.hpp"
|
#include "containerstore.hpp"
|
||||||
|
|
||||||
|
|
@ -89,8 +91,9 @@ namespace MWWorld
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(tooltip);
|
MWBase::Environment::get().getWindowManager()->messageBox(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update animation object
|
auto world = MWBase::Environment::get().getWorld();
|
||||||
MWBase::Environment::get().getWorld()->disable(target);
|
MWRender::Animation* anim = world->getAnimation(target);
|
||||||
MWBase::Environment::get().getWorld()->enable(target);
|
if (anim != nullptr)
|
||||||
|
anim->harvest(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1354,6 +1354,15 @@ namespace MWWorld
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CellStore* MWWorld::CellStore::getOriginCell(const Ptr& object) const
|
||||||
|
{
|
||||||
|
MovedRefTracker::const_iterator found = mMovedHere.find(object.getBase());
|
||||||
|
if (found != mMovedHere.end())
|
||||||
|
return found->second;
|
||||||
|
|
||||||
|
return object.getCell();
|
||||||
|
}
|
||||||
|
|
||||||
Ptr CellStore::getPtr(ESM::RefId id)
|
Ptr CellStore::getPtr(ESM::RefId id)
|
||||||
{
|
{
|
||||||
if (mState == CellStore::State_Unloaded)
|
if (mState == CellStore::State_Unloaded)
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,8 @@ namespace MWWorld
|
||||||
|
|
||||||
Ptr getMovedActor(int actorId) const;
|
Ptr getMovedActor(int actorId) const;
|
||||||
|
|
||||||
|
CellStore* getOriginCell(const Ptr& object) const;
|
||||||
|
|
||||||
Ptr getPtr(ESM::RefId id);
|
Ptr getPtr(ESM::RefId id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ namespace Bsa
|
||||||
public:
|
public:
|
||||||
using BSAFile::getFilename;
|
using BSAFile::getFilename;
|
||||||
using BSAFile::getList;
|
using BSAFile::getList;
|
||||||
|
using BSAFile::getPath;
|
||||||
using BSAFile::open;
|
using BSAFile::open;
|
||||||
|
|
||||||
BA2DX10File();
|
BA2DX10File();
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ namespace Bsa
|
||||||
public:
|
public:
|
||||||
using BSAFile::getFilename;
|
using BSAFile::getFilename;
|
||||||
using BSAFile::getList;
|
using BSAFile::getList;
|
||||||
|
using BSAFile::getPath;
|
||||||
using BSAFile::open;
|
using BSAFile::open;
|
||||||
|
|
||||||
BA2GNRLFile();
|
BA2GNRLFile();
|
||||||
|
|
|
||||||
|
|
@ -84,15 +84,15 @@ namespace Bsa
|
||||||
protected:
|
protected:
|
||||||
bool mHasChanged = false;
|
bool mHasChanged = false;
|
||||||
|
|
||||||
|
/// True when an archive has been loaded
|
||||||
|
bool mIsLoaded = false;
|
||||||
|
|
||||||
/// Table of files in this archive
|
/// Table of files in this archive
|
||||||
FileList mFiles;
|
FileList mFiles;
|
||||||
|
|
||||||
/// Filename string buffer
|
/// Filename string buffer
|
||||||
std::vector<char> mStringBuf;
|
std::vector<char> mStringBuf;
|
||||||
|
|
||||||
/// True when an archive has been loaded
|
|
||||||
bool mIsLoaded;
|
|
||||||
|
|
||||||
/// Used for error messages
|
/// Used for error messages
|
||||||
std::filesystem::path mFilepath;
|
std::filesystem::path mFilepath;
|
||||||
|
|
||||||
|
|
@ -109,11 +109,6 @@ namespace Bsa
|
||||||
* -----------------------------------
|
* -----------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BSAFile()
|
|
||||||
: mIsLoaded(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~BSAFile()
|
virtual ~BSAFile()
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
|
|
@ -148,6 +143,11 @@ namespace Bsa
|
||||||
return Files::pathToUnicodeString(mFilepath);
|
return Files::pathToUnicodeString(mFilepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path& getPath() const
|
||||||
|
{
|
||||||
|
return mFilepath;
|
||||||
|
}
|
||||||
|
|
||||||
// checks version of BSA from file header
|
// checks version of BSA from file header
|
||||||
static BsaVersion detectVersion(const std::filesystem::path& filePath);
|
static BsaVersion detectVersion(const std::filesystem::path& filePath);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ namespace Bsa
|
||||||
public:
|
public:
|
||||||
using BSAFile::getFilename;
|
using BSAFile::getFilename;
|
||||||
using BSAFile::getList;
|
using BSAFile::getList;
|
||||||
|
using BSAFile::getPath;
|
||||||
using BSAFile::open;
|
using BSAFile::open;
|
||||||
|
|
||||||
CompressedBSAFile() = default;
|
CompressedBSAFile() = default;
|
||||||
|
|
|
||||||
|
|
@ -37,22 +37,20 @@ namespace
|
||||||
|
|
||||||
namespace fx
|
namespace fx
|
||||||
{
|
{
|
||||||
namespace
|
VFS::Path::Normalized Technique::makeFileName(std::string_view name)
|
||||||
{
|
{
|
||||||
VFS::Path::Normalized makeFilePath(std::string_view name)
|
std::string fileName(name);
|
||||||
{
|
fileName += '.';
|
||||||
std::string fileName(name);
|
fileName += Technique::sExt;
|
||||||
fileName += Technique::sExt;
|
VFS::Path::Normalized result(Technique::sSubdir);
|
||||||
VFS::Path::Normalized result(Technique::sSubdir);
|
result /= fileName;
|
||||||
result /= fileName;
|
return result;
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width,
|
Technique::Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager,
|
||||||
int height, bool ubo, bool supportsNormals)
|
VFS::Path::NormalizedView fileName, std::string name, int width, int height, bool ubo, bool supportsNormals)
|
||||||
: mName(std::move(name))
|
: mName(std::move(name))
|
||||||
, mFilePath(makeFilePath(mName))
|
, mFilePath(fileName)
|
||||||
, mLastModificationTime(std::filesystem::file_time_type::clock::now())
|
, mLastModificationTime(std::filesystem::file_time_type::clock::now())
|
||||||
, mWidth(width)
|
, mWidth(width)
|
||||||
, mHeight(height)
|
, mHeight(height)
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,8 @@ namespace fx
|
||||||
using UniformMap = std::vector<std::shared_ptr<Types::UniformBase>>;
|
using UniformMap = std::vector<std::shared_ptr<Types::UniformBase>>;
|
||||||
using RenderTargetMap = std::unordered_map<std::string_view, Types::RenderTarget>;
|
using RenderTargetMap = std::unordered_map<std::string_view, Types::RenderTarget>;
|
||||||
|
|
||||||
static constexpr std::string_view sExt = ".omwfx";
|
static constexpr std::string_view sExt = "omwfx";
|
||||||
static constexpr std::string_view sSubdir = "shaders";
|
static constexpr VFS::Path::NormalizedView sSubdir{ "shaders" };
|
||||||
|
|
||||||
enum class Status
|
enum class Status
|
||||||
{
|
{
|
||||||
|
|
@ -123,8 +123,10 @@ namespace fx
|
||||||
static constexpr FlagsType Flag_Disable_SunGlare = (1 << 4);
|
static constexpr FlagsType Flag_Disable_SunGlare = (1 << 4);
|
||||||
static constexpr FlagsType Flag_Hidden = (1 << 5);
|
static constexpr FlagsType Flag_Hidden = (1 << 5);
|
||||||
|
|
||||||
Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, std::string name, int width,
|
static VFS::Path::Normalized makeFileName(std::string_view name);
|
||||||
int height, bool ubo, bool supportsNormals);
|
|
||||||
|
Technique(const VFS::Manager& vfs, Resource::ImageManager& imageManager, VFS::Path::NormalizedView fileName,
|
||||||
|
std::string name, int width, int height, bool ubo, bool supportsNormals);
|
||||||
|
|
||||||
bool compile();
|
bool compile();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <osg/AlphaFunc>
|
#include <osg/AlphaFunc>
|
||||||
|
#include <osg/Capability>
|
||||||
#include <osg/ColorMaski>
|
#include <osg/ColorMaski>
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
|
|
@ -607,6 +608,9 @@ namespace Resource
|
||||||
if (!getSupportsNormalsRT())
|
if (!getSupportsNormalsRT())
|
||||||
return;
|
return;
|
||||||
stateset->setAttributeAndModes(new osg::ColorMaski(1, enabled, enabled, enabled, enabled));
|
stateset->setAttributeAndModes(new osg::ColorMaski(1, enabled, enabled, enabled, enabled));
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
stateset->setAttributeAndModes(new osg::Disablei(GL_BLEND, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Callback to read image files from the VFS.
|
/// @brief Callback to read image files from the VFS.
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@ namespace TestingOpenMW
|
||||||
|
|
||||||
Files::IStreamPtr open() override { return std::make_unique<std::stringstream>(mContent, std::ios_base::in); }
|
Files::IStreamPtr open() override { return std::make_unique<std::stringstream>(mContent, std::ios_base::in); }
|
||||||
|
|
||||||
std::filesystem::path getPath() override { return "TestFile"; }
|
std::filesystem::file_time_type getLastModified() const override { return {}; }
|
||||||
|
|
||||||
|
std::string getStem() const override { return "TestFile"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string mContent;
|
const std::string mContent;
|
||||||
|
|
|
||||||
|
|
@ -10,45 +10,72 @@
|
||||||
#include <components/bsa/bsafile.hpp>
|
#include <components/bsa/bsafile.hpp>
|
||||||
#include <components/bsa/compressedbsafile.hpp>
|
#include <components/bsa/compressedbsafile.hpp>
|
||||||
|
|
||||||
|
#include <components/toutf8/toutf8.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
{
|
{
|
||||||
|
template <typename BSAFileType>
|
||||||
|
class BsaArchive;
|
||||||
|
|
||||||
template <typename FileType>
|
template <typename FileType>
|
||||||
class BsaArchiveFile : public File
|
class BsaArchiveFile : public File
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, FileType* bsa)
|
BsaArchiveFile(const Bsa::BSAFile::FileStruct* info, const BsaArchive<FileType>* bsa)
|
||||||
: mInfo(info)
|
: mInfo(info)
|
||||||
, mFile(bsa)
|
, mFile(bsa)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Files::IStreamPtr open() override { return mFile->getFile(mInfo); }
|
Files::IStreamPtr open() override { return mFile->getFile()->getFile(mInfo); }
|
||||||
|
|
||||||
std::filesystem::path getPath() override { return mInfo->name(); }
|
std::filesystem::file_time_type getLastModified() const override
|
||||||
|
{
|
||||||
|
return std::filesystem::last_write_time(mFile->getFile()->getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getStem() const override
|
||||||
|
{
|
||||||
|
std::string_view name = mInfo->name();
|
||||||
|
auto index = name.find_last_of("\\/");
|
||||||
|
if (index != std::string_view::npos)
|
||||||
|
name = name.substr(index + 1);
|
||||||
|
index = name.find_last_of('.');
|
||||||
|
if (index != std::string_view::npos && index != 0)
|
||||||
|
name = name.substr(0, index);
|
||||||
|
std::string out;
|
||||||
|
std::string_view utf8 = mFile->getUtf8(name, out);
|
||||||
|
if (out.data() == utf8.data())
|
||||||
|
out.resize(utf8.size());
|
||||||
|
else
|
||||||
|
out = utf8;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
const Bsa::BSAFile::FileStruct* mInfo;
|
const Bsa::BSAFile::FileStruct* mInfo;
|
||||||
FileType* mFile;
|
const BsaArchive<FileType>* mFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename BSAFileType>
|
template <typename BSAFileType>
|
||||||
class BsaArchive : public Archive
|
class BsaArchive : public Archive
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BsaArchive(const std::filesystem::path& filename)
|
BsaArchive(const std::filesystem::path& filename, const ToUTF8::StatelessUtf8Encoder* encoder)
|
||||||
: Archive()
|
: Archive()
|
||||||
|
, mEncoder(encoder)
|
||||||
{
|
{
|
||||||
mFile = std::make_unique<BSAFileType>();
|
mFile = std::make_unique<BSAFileType>();
|
||||||
mFile->open(filename);
|
mFile->open(filename);
|
||||||
|
|
||||||
const Bsa::BSAFile::FileList& filelist = mFile->getList();
|
std::string buffer;
|
||||||
for (Bsa::BSAFile::FileList::const_iterator it = filelist.begin(); it != filelist.end(); ++it)
|
for (const Bsa::BSAFile::FileStruct& file : mFile->getList())
|
||||||
{
|
{
|
||||||
mResources.emplace_back(&*it, mFile.get());
|
mResources.emplace_back(&file, this);
|
||||||
mFiles.emplace_back(it->name());
|
mFiles.emplace_back(getUtf8(file.name(), buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(mFiles.begin(), mFiles.end());
|
std::sort(mFiles.begin(), mFiles.end());
|
||||||
|
|
@ -56,8 +83,12 @@ namespace VFS
|
||||||
|
|
||||||
void listResources(FileMap& out) override
|
void listResources(FileMap& out) override
|
||||||
{
|
{
|
||||||
|
std::string buffer;
|
||||||
for (auto& resource : mResources)
|
for (auto& resource : mResources)
|
||||||
out[VFS::Path::Normalized(resource.mInfo->name())] = &resource;
|
{
|
||||||
|
std::string_view path = getUtf8(resource.mInfo->name(), buffer);
|
||||||
|
out[VFS::Path::Normalized(path)] = &resource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(Path::NormalizedView file) const override
|
bool contains(Path::NormalizedView file) const override
|
||||||
|
|
@ -67,26 +98,37 @@ namespace VFS
|
||||||
|
|
||||||
std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); }
|
std::string getDescription() const override { return std::string{ "BSA: " } + mFile->getFilename(); }
|
||||||
|
|
||||||
|
BSAFileType* getFile() const { return mFile.get(); }
|
||||||
|
|
||||||
|
std::string_view getUtf8(std::string_view input, std::string& buffer) const
|
||||||
|
{
|
||||||
|
if (mEncoder == nullptr)
|
||||||
|
return input;
|
||||||
|
return mEncoder->getUtf8(input, ToUTF8::BufferAllocationPolicy::UseGrowFactor, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<BSAFileType> mFile;
|
std::unique_ptr<BSAFileType> mFile;
|
||||||
std::vector<BsaArchiveFile<BSAFileType>> mResources;
|
std::vector<BsaArchiveFile<BSAFileType>> mResources;
|
||||||
std::vector<VFS::Path::Normalized> mFiles;
|
std::vector<VFS::Path::Normalized> mFiles;
|
||||||
|
const ToUTF8::StatelessUtf8Encoder* mEncoder;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
|
inline std::unique_ptr<VFS::Archive> makeBsaArchive(
|
||||||
|
const std::filesystem::path& path, const ToUTF8::StatelessUtf8Encoder* encoder)
|
||||||
{
|
{
|
||||||
switch (Bsa::BSAFile::detectVersion(path))
|
switch (Bsa::BSAFile::detectVersion(path))
|
||||||
{
|
{
|
||||||
case Bsa::BsaVersion::Unknown:
|
case Bsa::BsaVersion::Unknown:
|
||||||
break;
|
break;
|
||||||
case Bsa::BsaVersion::Uncompressed:
|
case Bsa::BsaVersion::Uncompressed:
|
||||||
return std::make_unique<BsaArchive<Bsa::BSAFile>>(path);
|
return std::make_unique<BsaArchive<Bsa::BSAFile>>(path, encoder);
|
||||||
case Bsa::BsaVersion::Compressed:
|
case Bsa::BsaVersion::Compressed:
|
||||||
return std::make_unique<BsaArchive<Bsa::CompressedBSAFile>>(path);
|
return std::make_unique<BsaArchive<Bsa::CompressedBSAFile>>(path, encoder);
|
||||||
case Bsa::BsaVersion::BA2GNRL:
|
case Bsa::BsaVersion::BA2GNRL:
|
||||||
return std::make_unique<BsaArchive<Bsa::BA2GNRLFile>>(path);
|
return std::make_unique<BsaArchive<Bsa::BA2GNRLFile>>(path, encoder);
|
||||||
case Bsa::BsaVersion::BA2DX10:
|
case Bsa::BsaVersion::BA2DX10:
|
||||||
return std::make_unique<BsaArchive<Bsa::BA2DX10File>>(path);
|
return std::make_unique<BsaArchive<Bsa::BA2DX10File>>(path, encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'");
|
throw std::runtime_error("Unknown archive type '" + Files::pathToUnicodeString(path) + "'");
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define OPENMW_COMPONENTS_VFS_FILE_H
|
#define OPENMW_COMPONENTS_VFS_FILE_H
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <components/files/istreamptr.hpp>
|
#include <components/files/istreamptr.hpp>
|
||||||
|
|
||||||
|
|
@ -14,7 +15,9 @@ namespace VFS
|
||||||
|
|
||||||
virtual Files::IStreamPtr open() = 0;
|
virtual Files::IStreamPtr open() = 0;
|
||||||
|
|
||||||
virtual std::filesystem::path getPath() = 0;
|
virtual std::filesystem::file_time_type getLastModified() const = 0;
|
||||||
|
|
||||||
|
virtual std::string getStem() const = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,4 +81,14 @@ namespace VFS
|
||||||
return Files::openConstrainedFileStream(mPath);
|
return Files::openConstrainedFileStream(mPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::file_time_type FileSystemArchiveFile::getLastModified() const
|
||||||
|
{
|
||||||
|
return std::filesystem::last_write_time(mPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileSystemArchiveFile::getStem() const
|
||||||
|
{
|
||||||
|
return Files::pathToUnicodeString(mPath.stem());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ namespace VFS
|
||||||
|
|
||||||
Files::IStreamPtr open() override;
|
Files::IStreamPtr open() override;
|
||||||
|
|
||||||
std::filesystem::path getPath() override { return mPath; }
|
std::filesystem::file_time_type getLastModified() const override;
|
||||||
|
|
||||||
|
std::string getStem() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::filesystem::path mPath;
|
std::filesystem::path mPath;
|
||||||
|
|
|
||||||
|
|
@ -81,15 +81,20 @@ namespace VFS
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path Manager::getAbsoluteFileName(const std::filesystem::path& name) const
|
std::filesystem::file_time_type Manager::getLastModified(VFS::Path::NormalizedView name) const
|
||||||
{
|
{
|
||||||
std::string normalized = Files::pathToUnicodeString(name);
|
const auto found = mIndex.find(name);
|
||||||
Path::normalizeFilenameInPlace(normalized);
|
|
||||||
|
|
||||||
const auto found = mIndex.find(normalized);
|
|
||||||
if (found == mIndex.end())
|
if (found == mIndex.end())
|
||||||
throw std::runtime_error("Resource '" + normalized + "' is not found");
|
throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found");
|
||||||
return found->second->getPath();
|
return found->second->getLastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Manager::getStem(VFS::Path::NormalizedView name) const
|
||||||
|
{
|
||||||
|
const auto found = mIndex.find(name);
|
||||||
|
if (found == mIndex.end())
|
||||||
|
throw std::runtime_error("Resource '" + std::string(name.value()) + "' not found");
|
||||||
|
return found->second->getStem();
|
||||||
}
|
}
|
||||||
|
|
||||||
RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator(std::string_view path) const
|
RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator(std::string_view path) const
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,9 @@ namespace VFS
|
||||||
|
|
||||||
RecursiveDirectoryRange getRecursiveDirectoryIterator() const;
|
RecursiveDirectoryRange getRecursiveDirectoryIterator() const;
|
||||||
|
|
||||||
/// Retrieve the absolute path to the file
|
std::filesystem::file_time_type getLastModified(VFS::Path::NormalizedView name) const;
|
||||||
/// @note Throws an exception if the file can not be found.
|
// Equivalent to std::filesystem::path::stem. The result isn't normalized.
|
||||||
/// @note May be called from any thread once the index has been built.
|
std::string getStem(VFS::Path::NormalizedView name) const;
|
||||||
std::filesystem::path getAbsoluteFileName(const std::filesystem::path& name) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<Archive>> mArchives;
|
std::vector<std::unique_ptr<Archive>> mArchives;
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,27 @@ namespace VFS::Path
|
||||||
return stream << value.mValue;
|
return stream << value.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NormalizedView parent() const
|
||||||
|
{
|
||||||
|
NormalizedView p;
|
||||||
|
const std::size_t pos = mValue.find_last_of(separator);
|
||||||
|
if (pos != std::string_view::npos)
|
||||||
|
p.mValue = mValue.substr(0, pos);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view stem() const
|
||||||
|
{
|
||||||
|
std::string_view stem = mValue;
|
||||||
|
std::size_t pos = stem.find_last_of(separator);
|
||||||
|
if (pos != std::string_view::npos)
|
||||||
|
stem = stem.substr(pos + 1);
|
||||||
|
pos = stem.find_first_of(extensionSeparator);
|
||||||
|
if (pos != std::string_view::npos)
|
||||||
|
stem = stem.substr(0, pos);
|
||||||
|
return stem;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string_view mValue;
|
std::string_view mValue;
|
||||||
};
|
};
|
||||||
|
|
@ -259,6 +280,16 @@ namespace VFS::Path
|
||||||
return stream << value.mValue;
|
return stream << value.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NormalizedView parent() const
|
||||||
|
{
|
||||||
|
return NormalizedView(*this).parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view stem() const
|
||||||
|
{
|
||||||
|
return NormalizedView(*this).stem();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mValue;
|
std::string mValue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace VFS
|
||||||
{
|
{
|
||||||
|
|
||||||
void registerArchives(VFS::Manager* vfs, const Files::Collections& collections,
|
void registerArchives(VFS::Manager* vfs, const Files::Collections& collections,
|
||||||
const std::vector<std::string>& archives, bool useLooseFiles)
|
const std::vector<std::string>& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder)
|
||||||
{
|
{
|
||||||
const Files::PathContainer& dataDirs = collections.getPaths();
|
const Files::PathContainer& dataDirs = collections.getPaths();
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace VFS
|
||||||
// Last BSA has the highest priority
|
// Last BSA has the highest priority
|
||||||
const auto archivePath = collections.getPath(*archive);
|
const auto archivePath = collections.getPath(*archive);
|
||||||
Log(Debug::Info) << "Adding BSA archive " << archivePath;
|
Log(Debug::Info) << "Adding BSA archive " << archivePath;
|
||||||
vfs->addArchive(makeBsaArchive(archivePath));
|
vfs->addArchive(makeBsaArchive(archivePath, encoder));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,18 @@
|
||||||
|
|
||||||
#include <components/files/collections.hpp>
|
#include <components/files/collections.hpp>
|
||||||
|
|
||||||
|
namespace ToUTF8
|
||||||
|
{
|
||||||
|
class StatelessUtf8Encoder;
|
||||||
|
}
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
{
|
{
|
||||||
class Manager;
|
class Manager;
|
||||||
|
|
||||||
/// @brief Register BSA and file system archives based on the given OpenMW configuration.
|
/// @brief Register BSA and file system archives based on the given OpenMW configuration.
|
||||||
void registerArchives(VFS::Manager* vfs, const Files::Collections& collections,
|
void registerArchives(VFS::Manager* vfs, const Files::Collections& collections,
|
||||||
const std::vector<std::string>& archives, bool useLooseFiles);
|
const std::vector<std::string>& archives, bool useLooseFiles, const ToUTF8::StatelessUtf8Encoder* encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
19
docs/source/_ext/omw-lexers.py
Normal file
19
docs/source/_ext/omw-lexers.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
from pygments.lexer import RegexLexer, bygroups
|
||||||
|
from pygments.token import Comment, Name, Operator, String, Text
|
||||||
|
from sphinx.highlighting import lexers
|
||||||
|
|
||||||
|
class OMWConfigLexer(RegexLexer):
|
||||||
|
name = 'openmwcfg'
|
||||||
|
aliases = ['openmwcfg']
|
||||||
|
filenames = ['openmw.cfg']
|
||||||
|
|
||||||
|
tokens = {
|
||||||
|
'root': [
|
||||||
|
(r'(\s*)(#.*$)', bygroups(Text.Whitespace, Comment.Single)),
|
||||||
|
(r'(\s*)([a-zA-Z0-9_.+-]+)(\s*(\+)?=\s*)(.*)', bygroups(Text.Whitespace, Name.Attribute, Operator, Operator, String)),
|
||||||
|
(r'.+\n', Text),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(_):
|
||||||
|
lexers["openmwcfg"] = OMWConfigLexer()
|
||||||
|
|
@ -138,3 +138,17 @@ tbody tr:hover {
|
||||||
#left-sidebar {
|
#left-sidebar {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#content div[class^=highlight], #content pre.literal-block, p, h4, h5, h6 {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size: 1.08rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,8 @@ extensions = [
|
||||||
'sphinx.ext.viewcode',
|
'sphinx.ext.viewcode',
|
||||||
'sphinx.ext.autosectionlabel',
|
'sphinx.ext.autosectionlabel',
|
||||||
'sphinx_design',
|
'sphinx_design',
|
||||||
'omw-directives'
|
'omw-directives',
|
||||||
|
'omw-lexers',
|
||||||
]
|
]
|
||||||
|
|
||||||
#autosectionlabel_prefix_document = True
|
#autosectionlabel_prefix_document = True
|
||||||
|
|
@ -138,7 +139,7 @@ exclude_patterns = []
|
||||||
#show_authors = False
|
#show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'default'
|
||||||
pygments_style_dark = 'github-dark'
|
pygments_style_dark = 'github-dark'
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
|
|
||||||
|
|
@ -81,15 +81,15 @@ For Distributions Using `apt` (e.g., Ubuntu, Debian)
|
||||||
|
|
||||||
.. code:: console
|
.. code:: console
|
||||||
|
|
||||||
sudo apt update
|
$ sudo apt update
|
||||||
sudo apt install innoextract
|
$ sudo apt install innoextract
|
||||||
|
|
||||||
For macOS using Homebrew
|
For macOS using Homebrew
|
||||||
++++++++++++++++++++++++
|
++++++++++++++++++++++++
|
||||||
|
|
||||||
.. code:: console
|
.. code:: console
|
||||||
|
|
||||||
brew install innoextract
|
$ brew install innoextract
|
||||||
|
|
||||||
Once innoextract is installed, download the game from GOG. The downloaded file should be called ``setup_tes_morrowind_goty_2.0.0.7.exe`` or something similar. When ``innoextract`` is run on it, it will extract the files directly into the folder the ``setup.exe`` file is located. If you have a specific folder where you want it to be extracted to, for example in ``~/Documents/Games/Morrowind`` You can specify it with the ``-d`` flag.
|
Once innoextract is installed, download the game from GOG. The downloaded file should be called ``setup_tes_morrowind_goty_2.0.0.7.exe`` or something similar. When ``innoextract`` is run on it, it will extract the files directly into the folder the ``setup.exe`` file is located. If you have a specific folder where you want it to be extracted to, for example in ``~/Documents/Games/Morrowind`` You can specify it with the ``-d`` flag.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,53 @@ Example:
|
||||||
|
|
||||||
core.sendGlobalEvent('UseItem', {object = potion, actor = player, force = true})
|
core.sendGlobalEvent('UseItem', {object = potion, actor = player, force = true})
|
||||||
|
|
||||||
|
**ModifyStat**
|
||||||
|
|
||||||
|
Modify the corresponding stat.
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
-- Consume 10 magicka
|
||||||
|
actor:sendEvent('ModifyStat', {name = 'magicka', amount = -10})
|
||||||
|
|
||||||
|
**AddVfx**
|
||||||
|
|
||||||
|
Calls the corresponding method in openmw.animation
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
local eventParams = {
|
||||||
|
model = 'vfx_default',
|
||||||
|
options = {
|
||||||
|
textureOverride = effect.particle,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actor:sendEvent('AddVfx', eventParams)
|
||||||
|
|
||||||
|
**PlaySound3d**
|
||||||
|
|
||||||
|
Calls the corresponding function in openw.core on the target. Will use core.sound.playSoundFile3d instead of core.sound.playSound3d if you put `file` instead of `sound` in the event data.
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
actor:sendEvent('PlaySound3d', {sound = 'Open Lock'})
|
||||||
|
|
||||||
|
|
||||||
|
**BreakInvisibility**
|
||||||
|
|
||||||
|
Forces the actor to lose all active invisibility effects.
|
||||||
|
|
||||||
|
|
||||||
UI events
|
UI events
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
**ShowMessage**
|
||||||
|
|
||||||
|
If sent to a player, shows a message as if a call to ui.showMessage was made.
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
player:sendEvent('ShowMessage', {message = 'Lorem ipsum'})
|
||||||
|
|
||||||
**UiModeChanged**
|
**UiModeChanged**
|
||||||
|
|
||||||
Every time UI mode is changed built-in scripts send to player the event ``UiModeChanged`` with arguments ``oldMode, ``newMode`` (same as ``I.UI.getMode()``)
|
Every time UI mode is changed built-in scripts send to player the event ``UiModeChanged`` with arguments ``oldMode, ``newMode`` (same as ``I.UI.getMode()``)
|
||||||
|
|
@ -91,3 +135,36 @@ Global events that just call the corresponding function in `openmw.world`.
|
||||||
|
|
||||||
-- world.setSimulationTimeScale(scale)
|
-- world.setSimulationTimeScale(scale)
|
||||||
core.sendGlobalEvent('SetSimulationTimeScale', scale)
|
core.sendGlobalEvent('SetSimulationTimeScale', scale)
|
||||||
|
|
||||||
|
|
||||||
|
**SpawnVfx, PlaySound3d**
|
||||||
|
|
||||||
|
Calls the corresponding function in openw.core. Note that PlaySound3d will call core.sound.playSoundFile3d instead of core.sound.playSound3d if you put `file` instead of `sound` in the event data.
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
core.sendGlobalEvent('SpawnVfx', {position = hitPos, model = 'vfx_destructarea', options = {scale = 10}})
|
||||||
|
core.sendGlobalEvent('PlaySound3d', {sound = 'Open Lock', position = container.position})
|
||||||
|
|
||||||
|
**ConsumeItem**
|
||||||
|
|
||||||
|
Reduces stack size of an item by a given amount, removing the item completely if stack size is reduced to 0 or less.
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
core.sendGlobalEvent('ConsumeItem', {item = foobar, amount = 1})
|
||||||
|
|
||||||
|
**Lock**
|
||||||
|
|
||||||
|
Lock a container or door
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
core.sendGlobalEvent('Lock', {taret = selected, magnitude = 50})
|
||||||
|
|
||||||
|
**Unlock**
|
||||||
|
|
||||||
|
Unlock a container or door
|
||||||
|
|
||||||
|
.. code-block:: Lua
|
||||||
|
|
||||||
|
core.sendGlobalEvent('Unlock', {taret = selected})
|
||||||
|
|
|
||||||
|
|
@ -126,10 +126,12 @@ The options are:
|
||||||
|
|
||||||
Enable it in ``openmw.cfg`` the same way as any other mod:
|
Enable it in ``openmw.cfg`` the same way as any other mod:
|
||||||
|
|
||||||
::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
data=path/to/my_lua_mod
|
data=path/to/my_lua_mod
|
||||||
content=my_lua_mod.omwscripts # or content=my_lua_mod.omwaddon
|
# or content=my_lua_mod.omwaddon
|
||||||
|
content=my_lua_mod.omwscripts
|
||||||
|
|
||||||
Now every time the player presses "X" on a keyboard, a message is shown.
|
Now every time the player presses "X" on a keyboard, a message is shown.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,6 @@ definitions and events. At a minimum it needs to include at least animation
|
||||||
runforward: stop 4.433333
|
runforward: stop 4.433333
|
||||||
attack1: start 4.466667
|
attack1: start 4.466667
|
||||||
attack1: stop 5.433333
|
attack1: stop 5.433333
|
||||||
...
|
|
||||||
|
|
||||||
The textkeys file is placed in the same folder as the model and matches the model's name.
|
The textkeys file is placed in the same folder as the model and matches the model's name.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ the file path to the texture is incorrect and OpenMW can't find it.
|
||||||
To fix this you can open the exported ``.dae`` file in a text editor and check
|
To fix this you can open the exported ``.dae`` file in a text editor and check
|
||||||
the texture's filepath. In the example of this barrel model it's found on lines 13-17.
|
the texture's filepath. In the example of this barrel model it's found on lines 13-17.
|
||||||
|
|
||||||
.. code::
|
.. code-block:: xml
|
||||||
|
|
||||||
<library_images>
|
<library_images>
|
||||||
<image id="id-image-4" name="the_barrel">
|
<image id="id-image-4" name="the_barrel">
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ dungeons.
|
||||||
To use this feature the :ref:`soft particles` setting must be enabled.
|
To use this feature the :ref:`soft particles` setting must be enabled.
|
||||||
This setting can either be activated in the OpenMW launcher or changed in `settings.cfg`:
|
This setting can either be activated in the OpenMW launcher or changed in `settings.cfg`:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Shaders]
|
[Shaders]
|
||||||
soft particles = true
|
soft particles = true
|
||||||
|
|
@ -64,7 +65,8 @@ Blue and alpha channels are ignored.
|
||||||
To use this feature the :ref:`post processing <Post Processing>` setting must be enabled.
|
To use this feature the :ref:`post processing <Post Processing>` setting must be enabled.
|
||||||
This setting can either be activated in the OpenMW launcher, in-game, or changed in `settings.cfg`:
|
This setting can either be activated in the OpenMW launcher, in-game, or changed in `settings.cfg`:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Post Processing]
|
[Post Processing]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,8 @@ The behavior of such a model:
|
||||||
|
|
||||||
The actual state toggling time depends on the sunrise/sunset time settings in `openmw.cfg`:
|
The actual state toggling time depends on the sunrise/sunset time settings in `openmw.cfg`:
|
||||||
|
|
||||||
::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
fallback=Weather_Sunrise_Time,6
|
fallback=Weather_Sunrise_Time,6
|
||||||
fallback=Weather_Sunset_Time,18
|
fallback=Weather_Sunset_Time,18
|
||||||
|
|
@ -102,7 +103,8 @@ These settings lead to the "night" starting at 20:00 and ending at 6:00.
|
||||||
|
|
||||||
The engine checks if the weather is bright enough to support the "interior day" mode using the Glare_View setting. If it is >= 0.5, the engine considers the weather bright.
|
The engine checks if the weather is bright enough to support the "interior day" mode using the Glare_View setting. If it is >= 0.5, the engine considers the weather bright.
|
||||||
|
|
||||||
::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
fallback=Weather_Clear_Glare_View,1
|
fallback=Weather_Clear_Glare_View,1
|
||||||
fallback=Weather_Foggy_Glare_View,0.25
|
fallback=Weather_Foggy_Glare_View,0.25
|
||||||
|
|
@ -138,7 +140,8 @@ If you want to override walking animations, you should override ``xbase_anim_fem
|
||||||
|
|
||||||
To enable this feature, you should have this line in your settings.cfg:
|
To enable this feature, you should have this line in your settings.cfg:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Game]
|
[Game]
|
||||||
use additional anim sources = true
|
use additional anim sources = true
|
||||||
|
|
@ -157,7 +160,8 @@ This feature conflicts with old mods which use scripted scabbards, arrows with p
|
||||||
|
|
||||||
The minimum you need is the ``xbase_anim_sh.nif`` file from the `Weapon Sheathing`_ mod and this line in your settings.cfg:
|
The minimum you need is the ``xbase_anim_sh.nif`` file from the `Weapon Sheathing`_ mod and this line in your settings.cfg:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Game]
|
[Game]
|
||||||
weapon sheathing = true
|
weapon sheathing = true
|
||||||
|
|
@ -205,7 +209,8 @@ Skeleton extensions
|
||||||
|
|
||||||
It is possible to inject custom bones into actor skeletons:
|
It is possible to inject custom bones into actor skeletons:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Game]
|
[Game]
|
||||||
use additional anim sources = true
|
use additional anim sources = true
|
||||||
|
|
@ -323,14 +328,16 @@ General advices to create assets for this feature:
|
||||||
|
|
||||||
Groundcover mods can be registered in the openmw.cfg via "groundcover" entries instead of "content" ones:
|
Groundcover mods can be registered in the openmw.cfg via "groundcover" entries instead of "content" ones:
|
||||||
|
|
||||||
::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
groundcover=my_grass_mod.esp
|
groundcover=my_grass_mod.esp
|
||||||
|
|
||||||
Every static from such mod is treated as a groundcover object.
|
Every static from such mod is treated as a groundcover object.
|
||||||
Also groundcover detection should be enabled via settings.cfg:
|
Also groundcover detection should be enabled via settings.cfg:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Groundcover]
|
[Groundcover]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@ Fonts
|
||||||
|
|
||||||
Default UI font and font used in magic scrolls are defined in ``openmw.cfg``:
|
Default UI font and font used in magic scrolls are defined in ``openmw.cfg``:
|
||||||
|
|
||||||
fallback=Fonts_Font_0,MysticCards
|
.. code-block:: openmwcfg
|
||||||
fallback=Fonts_Font_2,DemonicLetters
|
:caption: openmw.cfg
|
||||||
|
|
||||||
|
fallback=Fonts_Font_0,MysticCards
|
||||||
|
fallback=Fonts_Font_2,DemonicLetters
|
||||||
|
|
||||||
When there are no ``Fonts_Font_*`` lines in user's ``openmw.cfg``, built-in TrueType fonts are used.
|
When there are no ``Fonts_Font_*`` lines in user's ``openmw.cfg``, built-in TrueType fonts are used.
|
||||||
Font used by console and another debug windows is not configurable (so ``Fonts_Font_1`` is unused).
|
Font used by console and another debug windows is not configurable (so ``Fonts_Font_1`` is unused).
|
||||||
|
|
@ -20,8 +23,11 @@ You can use --export-fonts command line option to write the converted font
|
||||||
|
|
||||||
They can be used instead of TrueType fonts if needed by specifying their ``.fnt`` files names in the ``openmw.cfg``. For example:
|
They can be used instead of TrueType fonts if needed by specifying their ``.fnt`` files names in the ``openmw.cfg``. For example:
|
||||||
|
|
||||||
fallback=Fonts_Font_0,magic_cards_regular
|
.. code-block:: openmwcfg
|
||||||
fallback=Fonts_Font_2,daedric_font
|
:caption: openmw.cfg
|
||||||
|
|
||||||
|
fallback=Fonts_Font_0,magic_cards_regular
|
||||||
|
fallback=Fonts_Font_2,daedric_font
|
||||||
|
|
||||||
In this example OpenMW will search for ``magic_cards_regular.fnt`` and ``daedric_font.fnt`` in the ``Fonts`` folder in data directories.
|
In this example OpenMW will search for ``magic_cards_regular.fnt`` and ``daedric_font.fnt`` in the ``Fonts`` folder in data directories.
|
||||||
If they are not found, built-in TrueType fonts will be used as a fallback.
|
If they are not found, built-in TrueType fonts will be used as a fallback.
|
||||||
|
|
@ -35,16 +41,22 @@ Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. Th
|
||||||
OpenMW has build-in TrueType fonts: MysticCards, DemonicLetters and DejaVuLGCSansMono, which are used by default.
|
OpenMW has build-in TrueType fonts: MysticCards, DemonicLetters and DejaVuLGCSansMono, which are used by default.
|
||||||
TrueType fonts are configured via ``openmw.cfg`` too:
|
TrueType fonts are configured via ``openmw.cfg`` too:
|
||||||
|
|
||||||
fallback=Fonts_Font_0,MysticCards
|
.. code-block:: openmwcfg
|
||||||
fallback=Fonts_Font_2,DemonicLetters
|
:caption: openmw.cfg
|
||||||
|
|
||||||
|
fallback=Fonts_Font_0,MysticCards
|
||||||
|
fallback=Fonts_Font_2,DemonicLetters
|
||||||
|
|
||||||
In this example, OpenMW will scan ``Fonts`` folder in data directories for ``.omwfont`` files.
|
In this example, OpenMW will scan ``Fonts`` folder in data directories for ``.omwfont`` files.
|
||||||
These files are XML files with schema provided by MyGUI. OpenMW uses ``.omwfont`` files which name (without extension) matches ``openmw.cfg`` entries.
|
These files are XML files with schema provided by MyGUI. OpenMW uses ``.omwfont`` files which name (without extension) matches ``openmw.cfg`` entries.
|
||||||
|
|
||||||
It is also possible to adjust the font size via ``settings.cfg`` file::
|
It is also possible to adjust the font size via ``settings.cfg`` file:
|
||||||
|
|
||||||
[GUI]
|
.. code-block:: ini
|
||||||
font size = 16
|
:caption: settings.cfg
|
||||||
|
|
||||||
|
[GUI]
|
||||||
|
font size = 16
|
||||||
|
|
||||||
The ``font size`` setting accepts clamped values in range from 12 to 18.
|
The ``font size`` setting accepts clamped values in range from 12 to 18.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ and ``data=`` tells OpenMW what folders to look for meshes, textures, audio,
|
||||||
and other assets. The required lines would look like this, but with the paths
|
and other assets. The required lines would look like this, but with the paths
|
||||||
of course different on your system.
|
of course different on your system.
|
||||||
|
|
||||||
.. code::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
content=template.omwgame
|
content=template.omwgame
|
||||||
data="/home/someuser/example-suite/data"
|
data="/home/someuser/example-suite/data"
|
||||||
|
|
@ -51,7 +52,8 @@ you need to remove or comment out the following lines from ``openmw.cfg``.
|
||||||
Not doing so will either produce errors or load Morrowind content, which you
|
Not doing so will either produce errors or load Morrowind content, which you
|
||||||
probably do not want when you are making your own game.
|
probably do not want when you are making your own game.
|
||||||
|
|
||||||
.. code::
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
fallback-archive=Morrowind.bsa
|
fallback-archive=Morrowind.bsa
|
||||||
fallback-archive=Tribunal.bsa
|
fallback-archive=Tribunal.bsa
|
||||||
|
|
@ -70,8 +72,10 @@ are instead assigned through ``settings.cfg``. These models are player and NPC
|
||||||
animations, and meshes for the sky. In ``settings.cfg`` used by your OpenMW
|
animations, and meshes for the sky. In ``settings.cfg`` used by your OpenMW
|
||||||
install, add the following lines under the ``[Models]`` section.
|
install, add the following lines under the ``[Models]`` section.
|
||||||
|
|
||||||
.. code::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
|
[Models]
|
||||||
xbaseanim = meshes/BasicPlayer.dae
|
xbaseanim = meshes/BasicPlayer.dae
|
||||||
baseanim = meshes/BasicPlayer.dae
|
baseanim = meshes/BasicPlayer.dae
|
||||||
xbaseanim1st = meshes/BasicPlayer.dae
|
xbaseanim1st = meshes/BasicPlayer.dae
|
||||||
|
|
@ -103,7 +107,7 @@ need to be copied to ``resources/mygui`` folder found in your OpenMW installatio
|
||||||
folder. Overwrite any files aready in this folder. These files provide the
|
folder. Overwrite any files aready in this folder. These files provide the
|
||||||
UI font, its definition, and some minor UI tweaks.
|
UI font, its definition, and some minor UI tweaks.
|
||||||
|
|
||||||
.. code::
|
.. code-block:: none
|
||||||
|
|
||||||
openmw_box.skin.xml
|
openmw_box.skin.xml
|
||||||
openmw_button.skin.xml
|
openmw_button.skin.xml
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,8 @@ This can't change until computers are able to read minds.
|
||||||
Lines with options have an option name, then an equals sign (``=``), then an option value.
|
Lines with options have an option name, then an equals sign (``=``), then an option value.
|
||||||
Option names and values have leading and trailing whitespace trimmed, but whitespace within an option value is preserved - it's only removed if it's at the ends.
|
Option names and values have leading and trailing whitespace trimmed, but whitespace within an option value is preserved - it's only removed if it's at the ends.
|
||||||
This means that these are all equivalent:
|
This means that these are all equivalent:
|
||||||
::
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
|
||||||
data=some/dir
|
data=some/dir
|
||||||
data=some/dir
|
data=some/dir
|
||||||
|
|
@ -226,7 +227,10 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file
|
||||||
By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it.
|
By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it.
|
||||||
However, for this kind of install, it's okay to do so, so you can remove this warning.
|
However, for this kind of install, it's okay to do so, so you can remove this warning.
|
||||||
|
|
||||||
Change the start of the file from::
|
Change the start of the file from:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
# This is the local openmw.cfg file. Do not modify!
|
# This is the local openmw.cfg file. Do not modify!
|
||||||
# Modifications should be done on the user openmw.cfg file instead
|
# Modifications should be done on the user openmw.cfg file instead
|
||||||
|
|
@ -243,7 +247,10 @@ Change the start of the file from::
|
||||||
fallback=LightAttenuation_ConstantValue,0.0
|
fallback=LightAttenuation_ConstantValue,0.0
|
||||||
fallback=LightAttenuation_UseLinear,1
|
fallback=LightAttenuation_UseLinear,1
|
||||||
|
|
||||||
to::
|
to:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
data-local=userdata/data
|
data-local=userdata/data
|
||||||
user-data=userdata
|
user-data=userdata
|
||||||
|
|
@ -274,7 +281,10 @@ Navigate to the OpenMW installation directory, and open the ``openmw.cfg`` file
|
||||||
By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it.
|
By default, this contains a warning at the top telling you that this is the local ``openmw.cfg`` and not to modify it.
|
||||||
However, you'll need to make a small change to create this kind of install.
|
However, you'll need to make a small change to create this kind of install.
|
||||||
|
|
||||||
Change the start of the file from::
|
Change the start of the file from:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
# This is the local openmw.cfg file. Do not modify!
|
# This is the local openmw.cfg file. Do not modify!
|
||||||
# Modifications should be done on the user openmw.cfg file instead
|
# Modifications should be done on the user openmw.cfg file instead
|
||||||
|
|
@ -291,7 +301,10 @@ Change the start of the file from::
|
||||||
fallback=LightAttenuation_ConstantValue,0.0
|
fallback=LightAttenuation_ConstantValue,0.0
|
||||||
fallback=LightAttenuation_UseLinear,1
|
fallback=LightAttenuation_UseLinear,1
|
||||||
|
|
||||||
to::
|
to:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
# This is the local openmw.cfg file. Do not modify!
|
# This is the local openmw.cfg file. Do not modify!
|
||||||
# Modifications should be done on the user openmw.cfg file instead
|
# Modifications should be done on the user openmw.cfg file instead
|
||||||
|
|
@ -330,7 +343,10 @@ From scratch
|
||||||
Start by installing OpenMW in the usual way.
|
Start by installing OpenMW in the usual way.
|
||||||
Don't bother with first-time setup (i.e. telling it the location of an existing *Morrowind* installation).
|
Don't bother with first-time setup (i.e. telling it the location of an existing *Morrowind* installation).
|
||||||
|
|
||||||
In the default configuration directory (see `Configuration files and log files`_), create a file called ``openmw.cfg`` containing just::
|
In the default configuration directory (see `Configuration files and log files`_), create a file called ``openmw.cfg`` containing just
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
# select the game profile
|
# select the game profile
|
||||||
config=Morrowind
|
config=Morrowind
|
||||||
|
|
@ -340,7 +356,10 @@ This will put the basic setup required to play *Morrowind* into a new ``Morrowin
|
||||||
|
|
||||||
Next, come up with a name for the subprofile you'll create for your mod list.
|
Next, come up with a name for the subprofile you'll create for your mod list.
|
||||||
If you're following a modding guide, they've probably already given it a name, e.g. *Total Overhaul*, so that's the example we'll use.
|
If you're following a modding guide, they've probably already given it a name, e.g. *Total Overhaul*, so that's the example we'll use.
|
||||||
Add a line to the ``Morrowind/openmw.cfg`` with the profile name, e.g.::
|
Add a line to the ``Morrowind/openmw.cfg`` with the profile name like this:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: Morrowind/openmw.cfg
|
||||||
|
|
||||||
# select the mod list profile
|
# select the mod list profile
|
||||||
config=Total Overhaul
|
config=Total Overhaul
|
||||||
|
|
@ -356,7 +375,10 @@ The ones in the ``Morrowind`` directory are used for all profiles for *Morrowind
|
||||||
The ones in the ``Morrowind/Total Overhaul`` directory are only used for the *Total Overhaul* profile, so you can set up that mod list and any settings it requires here, and they won't affect any other profiles you set up later.
|
The ones in the ``Morrowind/Total Overhaul`` directory are only used for the *Total Overhaul* profile, so you can set up that mod list and any settings it requires here, and they won't affect any other profiles you set up later.
|
||||||
Making changes within the launcher will affect these files and leave all the others alone.
|
Making changes within the launcher will affect these files and leave all the others alone.
|
||||||
|
|
||||||
If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, e.g.::
|
If you want the *Total Overhaul* profile to keep its saved games etc. in a dedicated location instead of mixing them in with ones from another profile, you can add a ``user-data=…`` line to your ``Morrowind/Total Overhaul/openmw.cfg``, like this:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: Morrowind/Total Overhaul/openmw.cfg
|
||||||
|
|
||||||
# put saved games in a saves directory next to this file
|
# put saved games in a saves directory next to this file
|
||||||
user-data=.
|
user-data=.
|
||||||
|
|
@ -377,12 +399,18 @@ You'll now have an empty directory e.g. at ``Documents\My Games\OpenMW\Original`
|
||||||
Next, move all the files that were already in the default configuration directory to the profile directory you just made.
|
Next, move all the files that were already in the default configuration directory to the profile directory you just made.
|
||||||
Afterwards, the default configuration directory should only contain the profile directory you made.
|
Afterwards, the default configuration directory should only contain the profile directory you made.
|
||||||
|
|
||||||
Create a new ``openmw.cfg`` file in the default configuration directory containing::
|
Create a new ``openmw.cfg`` file in the default configuration directory containing:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
# select the profile
|
# select the profile
|
||||||
config=Original
|
config=Original
|
||||||
|
|
||||||
In the ``openmw.cfg`` in the profile directory, add these lines::
|
In the ``openmw.cfg`` in the profile directory, add these lines:
|
||||||
|
|
||||||
|
.. code-block:: openmwcfg
|
||||||
|
:caption: openmw.cfg
|
||||||
|
|
||||||
data-local=data
|
data-local=data
|
||||||
user-data=.
|
user-data=.
|
||||||
|
|
@ -402,9 +430,11 @@ Passing arguments on the command line lets you avoid this.
|
||||||
|
|
||||||
The basic idea is that you need to pass ``--replace config`` to ignore the configuration directories that the engine would have loaded because they were specified in ``openmw.cfg`` files, and pass each one you want to use instead with ``--config <directory path here>``.
|
The basic idea is that you need to pass ``--replace config`` to ignore the configuration directories that the engine would have loaded because they were specified in ``openmw.cfg`` files, and pass each one you want to use instead with ``--config <directory path here>``.
|
||||||
|
|
||||||
E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running::
|
E.g. if you've got a profile called *Morrowind* in your default configuration directory, and it's got a *Total Overhaul* subprofile, you could load it by running:
|
||||||
|
|
||||||
openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"
|
||||||
|
|
||||||
You can put this command into a script or shortcut and use it to easily launch OpenMW with that profile.
|
You can put this command into a script or shortcut and use it to easily launch OpenMW with that profile.
|
||||||
|
|
||||||
|
|
@ -422,15 +452,17 @@ On Windows, you can create a desktop shortcut to run this command with these ste
|
||||||
* At the end of that field, add the arguments for the profile you want, e.g. ``--replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"``.
|
* At the end of that field, add the arguments for the profile you want, e.g. ``--replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"``.
|
||||||
* Press *Apply* or *OK* to save the changes, and test the shortcut by double-clicking it.
|
* Press *Apply* or *OK* to save the changes, and test the shortcut by double-clicking it.
|
||||||
|
|
||||||
On most Linux distros, you can create a ``.desktop`` file like this::
|
On most Linux distros, you can create a ``.desktop`` file like this:
|
||||||
|
|
||||||
[Desktop Entry]
|
.. code-block:: desktop
|
||||||
Type=Application
|
|
||||||
Name=OpenMW - Total Overhaul
|
[Desktop Entry]
|
||||||
GenericName=Role Playing Game
|
Type=Application
|
||||||
Comment=OpenMW with the Total Overhaul profile
|
Name=OpenMW - Total Overhaul
|
||||||
Keywords=Morrowind;Reimplementation Mods;esm;bsa;
|
GenericName=Role Playing Game
|
||||||
TryExec=openmw
|
Comment=OpenMW with the Total Overhaul profile
|
||||||
Exec=openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"
|
Keywords=Morrowind;Reimplementation Mods;esm;bsa;
|
||||||
Icon=openmw
|
TryExec=openmw
|
||||||
Categories=Game;RolePlaying;
|
Exec=openmw --replace config --config ?userconfig?/Morrowind --config "?userconfig?/Morrowind/Total Overhaul"
|
||||||
|
Icon=openmw
|
||||||
|
Categories=Game;RolePlaying;
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,9 @@
|
||||||
Normal maps from Morrowind to OpenMW
|
Normal maps from Morrowind to OpenMW
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
- `General introduction to normal map conversion`_
|
|
||||||
- `OpenMW normal-mapping`_
|
|
||||||
- `Activating normal-mapping shaders in OpenMW`_
|
|
||||||
- `Morrowind bump-mapping`_
|
|
||||||
- `MGE XE normal-mapping`_
|
|
||||||
- `Converting PeterBitt's Scamp Replacer`_ (Mod made for the MGE XE PBR prototype)
|
|
||||||
- `Tutorial - MGE`_
|
|
||||||
- `Converting Lougian's Hlaalu Bump mapped`_ (Morrowind's bump-mapping, part 1: *without* custom models)
|
|
||||||
- `Tutorial - Morrowind, Part 1`_
|
|
||||||
- `Converting Apel's Various Things - Sacks`_ (Morrowind's bump-mapping, part 2: *with* custom models)
|
|
||||||
- `Tutorial - Morrowind, Part 2`_
|
|
||||||
|
|
||||||
General introduction to normal map conversion
|
General introduction to normal map conversion
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov
|
|
||||||
:Updated: 2020-03-03
|
|
||||||
|
|
||||||
This page has general information and tutorials on how normal-mapping works in OpenMW and how you can make mods using
|
This page has general information and tutorials on how normal-mapping works in OpenMW and how you can make mods using
|
||||||
the old environment-mapped bump-mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most
|
the old environment-mapped bump-mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most
|
||||||
(in)famous one to previously give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work better in OpenMW.
|
(in)famous one to previously give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work better in OpenMW.
|
||||||
|
|
@ -58,14 +43,15 @@ Activating normal-mapping shaders in OpenMW
|
||||||
Before normal (and specular and parallax) maps can show up in OpenMW, their auto-detection needs to be turned on in
|
Before normal (and specular and parallax) maps can show up in OpenMW, their auto-detection needs to be turned on in
|
||||||
settings.cfg_-file. Add these rows where it would make sense:
|
settings.cfg_-file. Add these rows where it would make sense:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Shaders]
|
[Shaders]
|
||||||
auto use object normal maps = true
|
auto use object normal maps = true
|
||||||
auto use terrain normal maps = true
|
auto use terrain normal maps = true
|
||||||
|
|
||||||
auto use object specular maps = true
|
auto use object specular maps = true
|
||||||
auto use terrain specular maps = true
|
auto use terrain specular maps = true
|
||||||
|
|
||||||
See OpenMW's wiki page about `texture modding`_ to read more about it.
|
See OpenMW's wiki page about `texture modding`_ to read more about it.
|
||||||
|
|
||||||
|
|
@ -81,10 +67,11 @@ are processed which makes bump-mapped models look a bit better,
|
||||||
can make use of the gloss map channel in the bump map and can apply bump-mapping to skinned models.
|
can make use of the gloss map channel in the bump map and can apply bump-mapping to skinned models.
|
||||||
Add this to settings.cfg_-file:
|
Add this to settings.cfg_-file:
|
||||||
|
|
||||||
::
|
.. code-block:: ini
|
||||||
|
:caption: settings.cfg
|
||||||
|
|
||||||
[Shaders]
|
[Shaders]
|
||||||
apply lighting to environment maps = true
|
apply lighting to environment maps = true
|
||||||
|
|
||||||
But sometimes you may want them to look a bit better than in vanilla.
|
But sometimes you may want them to look a bit better than in vanilla.
|
||||||
Technically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well,
|
Technically you aren't supposed to convert bump maps because they shouldn't be normal maps that are supported by OpenMW as well,
|
||||||
|
|
@ -117,9 +104,6 @@ Converting PeterBitt's Scamp Replacer
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
**Mod made for the MGE XE PBR prototype**
|
**Mod made for the MGE XE PBR prototype**
|
||||||
|
|
||||||
:Authors: Joakim (Lysol) Berg
|
|
||||||
:Updated: 2016-11-11
|
|
||||||
|
|
||||||
So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering
|
So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering
|
||||||
(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly
|
(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly
|
||||||
disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers.
|
disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers.
|
||||||
|
|
@ -161,9 +145,6 @@ Converting Lougian's Hlaalu Bump mapped
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
**Mod made for Morrowind's bump-mapping, without custom models**
|
**Mod made for Morrowind's bump-mapping, without custom models**
|
||||||
|
|
||||||
:Authors: Joakim (Lysol) Berg, Alexei (Capo) Dobrohotov
|
|
||||||
:Updated: 2020-03-03
|
|
||||||
|
|
||||||
Converting normal maps made for the Morrowind's bump-mapping can be really easy or a real pain,
|
Converting normal maps made for the Morrowind's bump-mapping can be really easy or a real pain,
|
||||||
depending on a few circumstances. In this tutorial, we will look at a very easy,
|
depending on a few circumstances. In this tutorial, we will look at a very easy,
|
||||||
although in some cases a bit time-consuming, example.
|
although in some cases a bit time-consuming, example.
|
||||||
|
|
@ -192,9 +173,6 @@ Converting Apel's Various Things - Sacks
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
**Mod made for Morrowind bump-mapping, with custom models**
|
**Mod made for Morrowind bump-mapping, with custom models**
|
||||||
|
|
||||||
:Authors: Joakim (Lysol) Berg, Alexei (Capostrophic) Dobrohotov
|
|
||||||
:Updated: 2020-03-03
|
|
||||||
|
|
||||||
In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``)
|
In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``)
|
||||||
files so that the bump maps could be loaded as normal maps.
|
files so that the bump maps could be loaded as normal maps.
|
||||||
We ignored those model files since they are not needed with OpenMW. In this tutorial however,
|
We ignored those model files since they are not needed with OpenMW. In this tutorial however,
|
||||||
|
|
|
||||||
|
|
@ -55,17 +55,20 @@ Simply create the textures with appropriate naming convention
|
||||||
the normal map would have to be called foo_n.dds).
|
the normal map would have to be called foo_n.dds).
|
||||||
To enable this automatic use based on filename pattern,
|
To enable this automatic use based on filename pattern,
|
||||||
you will have to add the following to your
|
you will have to add the following to your
|
||||||
`settings.cfg </source/reference/modding/paths>`_ file::
|
`settings.cfg </source/reference/modding/paths>`_ file:
|
||||||
|
|
||||||
[Shaders]
|
.. code-block:: ini
|
||||||
auto use object normal maps = true
|
:caption: settings.cfg
|
||||||
|
|
||||||
auto use object specular maps = true
|
[Shaders]
|
||||||
|
auto use object normal maps = true
|
||||||
|
|
||||||
normal map pattern = _n
|
auto use object specular maps = true
|
||||||
normal height map pattern = _nh
|
|
||||||
|
|
||||||
specular map pattern = _spec
|
normal map pattern = _n
|
||||||
|
normal height map pattern = _nh
|
||||||
|
|
||||||
|
specular map pattern = _spec
|
||||||
|
|
||||||
Additionally, a normal map with the `_nh` pattern enables
|
Additionally, a normal map with the `_nh` pattern enables
|
||||||
the use of the normal map's alpha channel as height information.
|
the use of the normal map's alpha channel as height information.
|
||||||
|
|
@ -92,18 +95,21 @@ For example, if you wanted to add specular mapping to a terrain layer called roc
|
||||||
you would copy this texture to a new file called rock_diffusespec.dds,
|
you would copy this texture to a new file called rock_diffusespec.dds,
|
||||||
and then edit its alpha channel to set the specular intensity.
|
and then edit its alpha channel to set the specular intensity.
|
||||||
|
|
||||||
The relevant settings are::
|
The relevant settings are
|
||||||
|
|
||||||
[Shaders]
|
.. code-block:: ini
|
||||||
auto use terrain normal maps = true
|
:caption: settings.cfg
|
||||||
|
|
||||||
auto use terrain specular maps = true
|
[Shaders]
|
||||||
|
auto use terrain normal maps = true
|
||||||
|
|
||||||
terrain specular map pattern = _diffusespec
|
auto use terrain specular maps = true
|
||||||
|
|
||||||
# Also used for terrain normal maps
|
terrain specular map pattern = _diffusespec
|
||||||
normal map pattern = _n
|
|
||||||
normal height map pattern = _nh
|
# Also used for terrain normal maps
|
||||||
|
normal map pattern = _n
|
||||||
|
normal height map pattern = _nh
|
||||||
|
|
||||||
OSG native files
|
OSG native files
|
||||||
################
|
################
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,9 @@ set(BUILTIN_DATA_FILES
|
||||||
scripts/omw/console/local.lua
|
scripts/omw/console/local.lua
|
||||||
scripts/omw/console/player.lua
|
scripts/omw/console/player.lua
|
||||||
scripts/omw/console/menu.lua
|
scripts/omw/console/menu.lua
|
||||||
|
scripts/omw/mechanics/actorcontroller.lua
|
||||||
scripts/omw/mechanics/animationcontroller.lua
|
scripts/omw/mechanics/animationcontroller.lua
|
||||||
|
scripts/omw/mechanics/globalcontroller.lua
|
||||||
scripts/omw/mechanics/playercontroller.lua
|
scripts/omw/mechanics/playercontroller.lua
|
||||||
scripts/omw/settings/menu.lua
|
scripts/omw/settings/menu.lua
|
||||||
scripts/omw/music/actor.lua
|
scripts/omw/music/actor.lua
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ PLAYER: scripts/omw/input/actionbindings.lua
|
||||||
PLAYER: scripts/omw/input/smoothmovement.lua
|
PLAYER: scripts/omw/input/smoothmovement.lua
|
||||||
PLAYER: scripts/omw/input/gamepadcontrols.lua
|
PLAYER: scripts/omw/input/gamepadcontrols.lua
|
||||||
NPC,CREATURE: scripts/omw/ai.lua
|
NPC,CREATURE: scripts/omw/ai.lua
|
||||||
|
GLOBAL: scripts/omw/mechanics/globalcontroller.lua
|
||||||
|
CREATURE, NPC, PLAYER: scripts/omw/mechanics/actorcontroller.lua
|
||||||
|
|
||||||
# User interface
|
# User interface
|
||||||
PLAYER: scripts/omw/ui.lua
|
PLAYER: scripts/omw/ui.lua
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,7 @@
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- Crosshair -->
|
<!-- Crosshair -->
|
||||||
<Widget type="ImageBox" skin="HUD_Crosshair" position="0 0 32 32" align="Center Center" name="Crosshair">
|
<Widget type="ImageBox" skin="HUD_Crosshair" position="0 0 27 27" align="Center Center" name="Crosshair"/>
|
||||||
</Widget>
|
|
||||||
|
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,16 +272,34 @@
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- Birthsign tooltip -->
|
<!-- Birthsign tooltip -->
|
||||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="BirthSignToolTip">
|
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="BirthSignToolTip">
|
||||||
<Property key="AutoResize" value="true"/>
|
<Property key="AutoResize" value="true"/>
|
||||||
<Property key="Padding" value="8"/>
|
<Property key="Padding" value="10"/>
|
||||||
|
|
||||||
<!-- Birthsign image -->
|
<!-- Birthsign image -->
|
||||||
<Widget type="Widget" skin="MW_Box" position="18 13 263 137" align="Top HCenter">
|
<Widget type="Widget" skin="MW_Box" position="18 13 263 137" align="Top HCenter">
|
||||||
<Widget type="ImageBox" skin="ImageBox" position="2 2 259 133" name="BirthSignImage" align="Left Top"/>
|
<Widget type="ImageBox" skin="ImageBox" position="2 2 259 133" name="BirthSignImage" align="Top"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 154 284 138" align="Top" name="BirthSignText">
|
<Widget type="AutoSizedTextBox" skin="NormalText" align="Top" name="BirthSignName">
|
||||||
|
<Property key="TextAlign" value="Top HCenter"/>
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<Widget type="AutoSizedEditBox" skin="SandText" position="0 0 400 100" align="Top" name="BirthSignDescription">
|
||||||
|
<Property key="MultiLine" value="true"/>
|
||||||
|
<Property key="WordWrap" value="true"/>
|
||||||
|
<Property key="TextAlign" value="Left Top"/>
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<Widget type="AutoSizedTextBox" skin="NormalText" align="Top" name="BirthSignAbilities">
|
||||||
|
<Property key="TextAlign" value="Top HCenter"/>
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<Widget type="AutoSizedTextBox" skin="NormalText" align="Top" name="BirthSignPowers">
|
||||||
|
<Property key="TextAlign" value="Top HCenter"/>
|
||||||
|
</Widget>
|
||||||
|
|
||||||
|
<Widget type="AutoSizedTextBox" skin="NormalText" align="Top" name="BirthSignSpells">
|
||||||
<Property key="TextAlign" value="Top HCenter"/>
|
<Property key="TextAlign" value="Top HCenter"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
|
||||||
23
files/data/scripts/omw/mechanics/actorcontroller.lua
Normal file
23
files/data/scripts/omw/mechanics/actorcontroller.lua
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
local self = require('openmw.self')
|
||||||
|
local core = require('openmw.core')
|
||||||
|
local types = require('openmw.types')
|
||||||
|
local Actor = types.Actor
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventHandlers = {
|
||||||
|
ModifyStat = function(data)
|
||||||
|
local stat = Actor.stats.dynamic[data.stat](self)
|
||||||
|
stat.current = stat.current + data.amount
|
||||||
|
end,
|
||||||
|
PlaySound3d = function(data)
|
||||||
|
if data.sound then
|
||||||
|
core.sound.playSound3d(data.sound, self, data.options)
|
||||||
|
else
|
||||||
|
core.sound.playSoundFile3d(data.file, self, data.options)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
BreakInvisibility = function(data)
|
||||||
|
Actor.activeEffects(self):remove(core.magic.EFFECT_TYPE.Invisibility)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
39
files/data/scripts/omw/mechanics/globalcontroller.lua
Normal file
39
files/data/scripts/omw/mechanics/globalcontroller.lua
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
local types = require('openmw.types')
|
||||||
|
local Lockable = types.Lockable
|
||||||
|
local Item = require('openmw.types').Item
|
||||||
|
local world = require('openmw.world')
|
||||||
|
local core = require('openmw.core')
|
||||||
|
|
||||||
|
local function onConsumeItem(data)
|
||||||
|
local item = data.item
|
||||||
|
local amount = data.amount
|
||||||
|
if amount > item.count then
|
||||||
|
print('Warning: tried to consume '..tostring(amount)..' '..tostring(item)..'s, but there were only '..tostring(item.count))
|
||||||
|
amount = item.count
|
||||||
|
end
|
||||||
|
item:remove(amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onPlaySound3d(data)
|
||||||
|
if data.sound then
|
||||||
|
core.sound.playSound3d(data.sound, data.position, data.options)
|
||||||
|
elseif data.file then
|
||||||
|
core.sound.playSoundFile3d(data.file, data.position, data.options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventHandlers = {
|
||||||
|
SpawnVfx = function(data)
|
||||||
|
world.vfx.spawn(data.model, data.position, data.options)
|
||||||
|
end,
|
||||||
|
PlaySound3d = onPlaySound3d,
|
||||||
|
ConsumeItem = onConsumeItem,
|
||||||
|
Lock = function(data)
|
||||||
|
Lockable.lock(data.target, data.magnitude)
|
||||||
|
end,
|
||||||
|
Unlock = function(data)
|
||||||
|
Lockable.unlock(data.target)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -111,4 +111,10 @@ return {
|
||||||
engineHandlers = {
|
engineHandlers = {
|
||||||
onUpdate = onUpdate,
|
onUpdate = onUpdate,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
eventHandlers = {
|
||||||
|
ShowMessage = function(data)
|
||||||
|
if data.message then ui.showMessage(data.message) end
|
||||||
|
end
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,13 @@ local function onUiModeChangedEvent(data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function isWindowVisible(windowName)
|
||||||
|
if replacedWindows[windowName] then
|
||||||
|
return replacedWindows[windowName].visible
|
||||||
|
end
|
||||||
|
return ui._isWindowVisible(windowName)
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
interfaceName = 'UI',
|
interfaceName = 'UI',
|
||||||
---
|
---
|
||||||
|
|
@ -164,7 +171,7 @@ return {
|
||||||
interface = {
|
interface = {
|
||||||
--- Interface version
|
--- Interface version
|
||||||
-- @field [parent=#UI] #number version
|
-- @field [parent=#UI] #number version
|
||||||
version = 1,
|
version = 2,
|
||||||
|
|
||||||
--- All available UI modes.
|
--- All available UI modes.
|
||||||
-- Use `view(I.UI.MODE)` in `luap` console mode to see the list.
|
-- Use `view(I.UI.MODE)` in `luap` console mode to see the list.
|
||||||
|
|
@ -240,6 +247,13 @@ return {
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
isHudVisible = function() return ui._isHudVisible() end,
|
isHudVisible = function() return ui._isHudVisible() end,
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Returns if the given window is visible or not
|
||||||
|
-- @function [parent=#UI] isWindowVisible
|
||||||
|
-- @param #string windowName
|
||||||
|
-- @return #boolean
|
||||||
|
isWindowVisible = isWindowVisible,
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
-- registerHudElement = function(name, showFn, hideFn) end,
|
-- registerHudElement = function(name, showFn, hideFn) end,
|
||||||
-- showHudElement = function(name, bool) end,
|
-- showHudElement = function(name, bool) end,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue