mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-02 00:04:31 +00:00
luamanagerimp still needs float frameDuration, and reordering a bunch of fields in the scene manager conflicted with changing the type of maxanisotropy.
This commit is contained in:
commit
06cb00bb0b
39 changed files with 364 additions and 157 deletions
|
|
@ -1,3 +1,7 @@
|
|||
0.51.0
|
||||
------
|
||||
|
||||
|
||||
0.50.0
|
||||
------
|
||||
|
||||
|
|
@ -71,6 +75,7 @@
|
|||
Bug #8650: Some plants turn invisible when being called types.Container.inventory(cont):isResolved()
|
||||
Bug #8680: Dead ancestor ghosts stop being dust when you rest near them
|
||||
Bug #8686: openmw-cs: Crash when smoothing terrain of a not-yet-created cell.
|
||||
Bug #8710: Absorb Skill breaks on creatures
|
||||
Feature #2522: Support quick item transfer
|
||||
Feature #3740: Gamepad GUI Mode
|
||||
Feature #3769: Allow GetSpellEffects on enchantments
|
||||
|
|
|
|||
|
|
@ -80,9 +80,9 @@ endif()
|
|||
message(STATUS "Configuring OpenMW...")
|
||||
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 50)
|
||||
set(OPENMW_VERSION_MINOR 51)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 95)
|
||||
set(OPENMW_LUA_API_REVISION 96)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 3)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ OpenMW is an open-source open-world RPG game engine that supports playing Morrow
|
|||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.
|
||||
|
||||
* Version: 0.50.0
|
||||
* Version: 0.51.0
|
||||
* License: GPLv3 (see [LICENSE](https://gitlab.com/OpenMW/openmw/-/raw/master/LICENSE) for more information)
|
||||
* Website: https://www.openmw.org
|
||||
* IRC: #openmw on irc.libera.chat
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ file(GLOB UNITTEST_SRC_FILES
|
|||
esmterrain/testgridsampling.cpp
|
||||
|
||||
resource/testobjectcache.cpp
|
||||
resource/testresourcesystem.cpp
|
||||
|
||||
vfs/testpathutil.cpp
|
||||
|
||||
|
|
|
|||
32
apps/components_tests/resource/testresourcesystem.cpp
Normal file
32
apps/components_tests/resource/testresourcesystem.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/toutf8/toutf8.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace testing;
|
||||
|
||||
TEST(ResourceResourceSystem, scenemanager_getinstance_should_be_thread_safe)
|
||||
{
|
||||
const VFS::Manager vfsManager;
|
||||
const ToUTF8::Utf8Encoder encoder(ToUTF8::WINDOWS_1252);
|
||||
Resource::ResourceSystem resourceSystem(&vfsManager, 1.0, &encoder.getStatelessEncoder());
|
||||
Resource::SceneManager* sceneManager = resourceSystem.getSceneManager();
|
||||
|
||||
constexpr VFS::Path::NormalizedView noSuchPath("meshes/whatever.nif");
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
threads.emplace_back([=]() { sceneManager->getInstance(noSuchPath); });
|
||||
}
|
||||
for (std::thread& thread : threads)
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
|
@ -146,6 +146,7 @@ namespace MWClass
|
|||
{
|
||||
// Do not allow equip tools from inventory during attack
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)
|
||||
&& !MWBase::Environment::get().getMechanicsManager()->isCastingSpell(npc)
|
||||
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return { 0, "#{sCantEquipWeapWarning}" };
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ namespace MWClass
|
|||
{
|
||||
// Do not allow equip tools from inventory during attack
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc)
|
||||
&& !MWBase::Environment::get().getMechanicsManager()->isCastingSpell(npc)
|
||||
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
return { 0, "#{sCantEquipWeapWarning}" };
|
||||
|
||||
|
|
|
|||
|
|
@ -270,10 +270,25 @@ namespace MWClass
|
|||
|
||||
std::pair<int, std::string_view> Weapon::canBeEquipped(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& npc) const
|
||||
{
|
||||
int type = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
|
||||
// Do not allow equip weapons from inventory during attack
|
||||
if (npc.isInCell() && MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||
&& MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
||||
return { 0, "#{sCantEquipWeapWarning}" };
|
||||
{
|
||||
int activeWeaponType = ESM::Weapon::None;
|
||||
MWMechanics::getActiveWeapon(npc, &activeWeaponType);
|
||||
if (activeWeaponType > ESM::Weapon::None || activeWeaponType == ESM::Weapon::HandToHand)
|
||||
{
|
||||
auto* activeWeapon = MWMechanics::getWeaponType(activeWeaponType);
|
||||
bool isAmmo = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Class::Ammo;
|
||||
bool activeWeapUsesAmmo = activeWeapon->mWeaponClass == ESM::WeaponType::Class::Ranged;
|
||||
bool sameAmmoType = activeWeapon->mAmmoType == type;
|
||||
// special case for ammo equipping
|
||||
if ((activeWeapUsesAmmo && !sameAmmoType) || !isAmmo)
|
||||
return { 0, "#{sCantEquipWeapWarning}" };
|
||||
}
|
||||
}
|
||||
|
||||
if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)
|
||||
return { 0, "#{sInventoryMessage1}" };
|
||||
|
|
@ -283,7 +298,6 @@ namespace MWClass
|
|||
if (slots.first.empty())
|
||||
return { 0, {} };
|
||||
|
||||
int type = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
if (MWMechanics::getWeaponType(type)->mFlags & ESM::WeaponType::TwoHanded)
|
||||
{
|
||||
return { 2, {} };
|
||||
|
|
|
|||
|
|
@ -325,10 +325,10 @@ namespace MWGui
|
|||
// If we unequip weapon during attack, it can lead to unexpected behaviour
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr))
|
||||
{
|
||||
bool isWeapon = item.mBase.getType() == ESM::Weapon::sRecordId;
|
||||
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
|
||||
|
||||
if (isWeapon && invStore.isEquipped(item.mBase))
|
||||
MWWorld::ContainerStoreIterator weapIt = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
bool weapActive = mPtr.getClass().getCreatureStats(mPtr).getDrawState() == MWMechanics::DrawState::Weapon;
|
||||
if (weapActive && weapIt != invStore.end() && *weapIt == item.mBase)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCantEquipWeapWarning}");
|
||||
return;
|
||||
|
|
@ -756,6 +756,8 @@ namespace MWGui
|
|||
|
||||
void InventoryWindow::onFrame(float dt)
|
||||
{
|
||||
updateEncumbranceBar();
|
||||
|
||||
if (mUpdateNextFrame)
|
||||
{
|
||||
if (mTrading)
|
||||
|
|
@ -766,7 +768,6 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->updateOffer();
|
||||
}
|
||||
|
||||
updateEncumbranceBar();
|
||||
mDragAndDrop->update();
|
||||
mItemView->update();
|
||||
notifyContentChanged();
|
||||
|
|
@ -969,25 +970,25 @@ namespace MWGui
|
|||
mControllerButtons.mA = "#{OMWEngine:InventorySelect}";
|
||||
mControllerButtons.mB = "#{Interface:Close}";
|
||||
mControllerButtons.mX.clear();
|
||||
mControllerButtons.mR2 = "#{sCompanionShare}";
|
||||
mControllerButtons.mR2 = "#{Interface:Share}";
|
||||
break;
|
||||
case MWGui::GM_Container:
|
||||
mControllerButtons.mA = "#{OMWEngine:InventorySelect}";
|
||||
mControllerButtons.mB = "#{Interface:Close}";
|
||||
mControllerButtons.mX = "#{Interface:TakeAll}";
|
||||
mControllerButtons.mR2 = "#{sContainer}";
|
||||
mControllerButtons.mR2 = "#{Interface:Container}";
|
||||
break;
|
||||
case MWGui::GM_Barter:
|
||||
mControllerButtons.mA = "#{sSell}";
|
||||
mControllerButtons.mA = "#{Interface:Sell}";
|
||||
mControllerButtons.mB = "#{Interface:Cancel}";
|
||||
mControllerButtons.mX = "#{Interface:Offer}";
|
||||
mControllerButtons.mR2 = "#{sBarter}";
|
||||
mControllerButtons.mR2 = "#{Interface:Barter}";
|
||||
break;
|
||||
case MWGui::GM_Inventory:
|
||||
default:
|
||||
mControllerButtons.mA = "#{sEquip}";
|
||||
mControllerButtons.mA = "#{Interface:Equip}";
|
||||
mControllerButtons.mB = "#{Interface:Back}";
|
||||
mControllerButtons.mX = "#{sDrop}";
|
||||
mControllerButtons.mX = "#{Interface:Drop}";
|
||||
mControllerButtons.mR2.clear();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -836,9 +836,10 @@ namespace MWGui
|
|||
if (Settings::gui().mControllerMenus)
|
||||
{
|
||||
mControllerButtons.mB = "#{Interface:Back}";
|
||||
mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}";
|
||||
mControllerButtons.mY = "#{sCenter}";
|
||||
mControllerButtons.mDpad = Settings::map().mAllowZooming ? "" : "#{sMove}";
|
||||
mControllerButtons.mX = global ? "#{Interface:Local}" : "#{Interface:World}";
|
||||
mControllerButtons.mY = "#{Interface:Center}";
|
||||
if (!Settings::map().mAllowZooming)
|
||||
mControllerButtons.mDpad = "#{Interface:Move}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1233,7 +1234,7 @@ namespace MWGui
|
|||
mLocalMap->setVisible(!global);
|
||||
|
||||
mButton->setCaptionWithReplacing(global ? "#{sLocal}" : "#{sWorld}");
|
||||
mControllerButtons.mX = global ? "#{sLocal}" : "#{sWorld}";
|
||||
mControllerButtons.mX = global ? "#{Interface:Local}" : "#{Interface:World}";
|
||||
MWBase::Environment::get().getWindowManager()->updateControllerButtonsOverlay();
|
||||
}
|
||||
|
||||
|
|
@ -1537,7 +1538,10 @@ namespace MWGui
|
|||
|
||||
ControllerButtons* EditNoteDialog::getControllerButtons()
|
||||
{
|
||||
mControllerButtons.mX = getDeleteButtonShown() ? "#{sDelete}" : "";
|
||||
if (getDeleteButtonShown())
|
||||
mControllerButtons.mX = "#{Interface:Delete}";
|
||||
else
|
||||
mControllerButtons.mX.clear();
|
||||
return &mControllerButtons;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ namespace MWGui
|
|||
mControllerButtons.mLStick = "#{Interface:Mouse}";
|
||||
mControllerButtons.mA = "#{Interface:Select}";
|
||||
mControllerButtons.mB = "#{Interface:Back}";
|
||||
mControllerButtons.mY = "#{sSex}";
|
||||
mControllerButtons.mL1 = "#{sHair}";
|
||||
mControllerButtons.mR1 = "#{sFace}";
|
||||
mControllerButtons.mY = "#{Interface:Sex}";
|
||||
mControllerButtons.mL1 = "#{Interface:Hair}";
|
||||
mControllerButtons.mR1 = "#{Interface:Face}";
|
||||
}
|
||||
|
||||
updateRaces();
|
||||
|
|
|
|||
|
|
@ -215,6 +215,12 @@ namespace MWInput
|
|||
|
||||
void ControllerManager::axisMoved(int deviceID, const SDL_ControllerAxisEvent& arg)
|
||||
{
|
||||
if (mBindingsManager->isDetectingBindingState())
|
||||
{
|
||||
mBindingsManager->controllerAxisMoved(deviceID, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Settings::input().mEnableController || MWBase::Environment::get().getInputManager()->controlsDisabled())
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,20 @@
|
|||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct BoolScopeGuard
|
||||
{
|
||||
bool& mValue;
|
||||
BoolScopeGuard(bool& value)
|
||||
: mValue(value)
|
||||
{
|
||||
mValue = true;
|
||||
}
|
||||
|
||||
~BoolScopeGuard() { mValue = false; }
|
||||
};
|
||||
}
|
||||
|
||||
static LuaUtil::LuaStateSettings createLuaStateSettings()
|
||||
{
|
||||
|
|
@ -264,31 +278,33 @@ namespace MWLua
|
|||
// can teleport the player to the starting location before the first frame is rendered.
|
||||
mGlobalScripts.newGameStarted();
|
||||
}
|
||||
BoolScopeGuard updateGuard(mRunningSynchronizedUpdates);
|
||||
|
||||
// We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency.
|
||||
mProcessingInputEvents = true;
|
||||
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
PlayerScripts* playerScripts
|
||||
= mPlayer.isEmpty() ? nullptr : dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
||||
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
for (const auto& event : mMenuInputEvents)
|
||||
mMenuScripts.processInputEvent(event);
|
||||
mMenuInputEvents.clear();
|
||||
if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu))
|
||||
// We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency.
|
||||
{
|
||||
for (const auto& event : mInputEvents)
|
||||
playerScripts->processInputEvent(event);
|
||||
BoolScopeGuard processingGuard(mProcessingInputEvents);
|
||||
|
||||
for (const auto& event : mMenuInputEvents)
|
||||
mMenuScripts.processInputEvent(event);
|
||||
mMenuInputEvents.clear();
|
||||
if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu))
|
||||
{
|
||||
for (const auto& event : mInputEvents)
|
||||
playerScripts->processInputEvent(event);
|
||||
}
|
||||
mInputEvents.clear();
|
||||
mLuaEvents.callMenuEventHandlers();
|
||||
float frameDuration = MWBase::Environment::get().getWorld()->getTimeManager()->isPaused()
|
||||
? 0.f
|
||||
: MWBase::Environment::get().getFrameDuration();
|
||||
mInputActions.update(frameDuration);
|
||||
mMenuScripts.onFrame(frameDuration);
|
||||
if (playerScripts)
|
||||
playerScripts->onFrame(frameDuration);
|
||||
}
|
||||
mInputEvents.clear();
|
||||
mLuaEvents.callMenuEventHandlers();
|
||||
float frameDuration = MWBase::Environment::get().getWorld()->getTimeManager()->isPaused()
|
||||
? 0.f
|
||||
: MWBase::Environment::get().getFrameDuration();
|
||||
mInputActions.update(frameDuration);
|
||||
mMenuScripts.onFrame(frameDuration);
|
||||
if (playerScripts)
|
||||
playerScripts->onFrame(frameDuration);
|
||||
mProcessingInputEvents = false;
|
||||
|
||||
for (const auto& [message, mode] : mUIMessages)
|
||||
windowManager->messageBox(message, mode);
|
||||
|
|
@ -316,7 +332,7 @@ namespace MWLua
|
|||
|
||||
void LuaManager::applyDelayedActions()
|
||||
{
|
||||
mApplyingDelayedActions = true;
|
||||
BoolScopeGuard applyingGuard(mApplyingDelayedActions);
|
||||
for (DelayedAction& action : mActionQueue)
|
||||
action.apply();
|
||||
mActionQueue.clear();
|
||||
|
|
@ -324,7 +340,6 @@ namespace MWLua
|
|||
if (mTeleportPlayerAction)
|
||||
mTeleportPlayerAction->apply();
|
||||
mTeleportPlayerAction.reset();
|
||||
mApplyingDelayedActions = false;
|
||||
}
|
||||
|
||||
void LuaManager::clear()
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace MWLua
|
|||
|
||||
// Some changes to the game world can not be done from the scripting thread (because it runs in parallel with
|
||||
// OSG Cull), so we need to queue it and apply from the main thread.
|
||||
void addAction(std::function<void()> action, std::string_view name = "");
|
||||
void addAction(std::function<void()> action, std::string_view name = {});
|
||||
void addTeleportPlayerAction(std::function<void()> action);
|
||||
|
||||
// Saving
|
||||
|
|
@ -174,6 +174,8 @@ namespace MWLua
|
|||
void sendLocalEvent(
|
||||
const MWWorld::Ptr& target, const std::string& name, const std::optional<sol::table>& data = std::nullopt);
|
||||
|
||||
bool isSynchronizedUpdateRunning() const { return mRunningSynchronizedUpdates; }
|
||||
|
||||
private:
|
||||
void initConfiguration();
|
||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr,
|
||||
|
|
@ -187,6 +189,7 @@ namespace MWLua
|
|||
bool mApplyingDelayedActions = false;
|
||||
bool mNewGameStarted = false;
|
||||
bool mReloadAllScriptsRequested = false;
|
||||
bool mRunningSynchronizedUpdates = false;
|
||||
LuaUtil::ScriptsConfiguration mConfiguration;
|
||||
LuaUtil::LuaState mLua;
|
||||
LuaUi::ResourceManager mUiResourceManager;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "../mwstate/character.hpp"
|
||||
|
||||
#include "context.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
|
@ -72,7 +73,9 @@ namespace MWLua
|
|||
return sol::nullopt;
|
||||
};
|
||||
|
||||
api["saveGame"] = [](std::string_view description, sol::optional<std::string_view> slotName) {
|
||||
api["saveGame"] = [context](std::string_view description, sol::optional<std::string_view> slotName) {
|
||||
if (!context.mLuaManager->isSynchronizedUpdateRunning())
|
||||
throw std::runtime_error("menu.saveGame can only be used during engine or event handler processing");
|
||||
MWBase::StateManager* manager = MWBase::Environment::get().getStateManager();
|
||||
const MWState::Character* character = manager->getCurrentCharacter();
|
||||
const MWState::Slot* slot = nullptr;
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ namespace MWMechanics
|
|||
else if (!godmode)
|
||||
{
|
||||
damageSkill(target, effect, effect.mMagnitude);
|
||||
if (!caster.isEmpty())
|
||||
if (!caster.isEmpty() && caster.getClass().isNpc())
|
||||
fortifySkill(caster, effect, effect.mMagnitude);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1303,7 +1303,7 @@ namespace MWMechanics
|
|||
{
|
||||
const auto caster = world->searchPtrViaActorId(spellParams.getCasterActorId());
|
||||
restoreSkill(target, effect, effect.mMagnitude);
|
||||
if (!caster.isEmpty())
|
||||
if (!caster.isEmpty() && caster.getClass().isNpc())
|
||||
fortifySkill(caster, effect, -effect.mMagnitude);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -659,15 +659,13 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
{
|
||||
const char* release;
|
||||
// Report the last version still capable of reading this save
|
||||
if (e.getFormatVersion() <= ESM::OpenMW0_48SaveGameFormatVersion)
|
||||
if (e.getFormatVersion() < ESM::OpenMW0_49MinSaveGameFormatVersion)
|
||||
release = "OpenMW 0.48.0";
|
||||
else if (e.getFormatVersion() <= ESM::OpenMW0_49SaveGameFormatVersion)
|
||||
release = "OpenMW 0.49.0";
|
||||
else
|
||||
{
|
||||
// Insert additional else if statements above to cover future releases
|
||||
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_50SaveGameFormatVersion);
|
||||
release = "OpenMW 0.50.0";
|
||||
static_assert(ESM::MinSupportedSaveGameFormatVersion <= ESM::OpenMW0_49MinSaveGameFormatVersion);
|
||||
release = "OpenMW 0.51.0";
|
||||
}
|
||||
auto l10n = MWBase::Environment::get().getL10nManager()->getContext("OMWEngine");
|
||||
std::string error = l10n->formatMessage("LoadingRequiresOldVersionError", { "version" }, { release });
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@ namespace ESM
|
|||
inline constexpr FormatVersion CurrentSaveGameFormatVersion = 34;
|
||||
|
||||
inline constexpr FormatVersion MinSupportedSaveGameFormatVersion = 5;
|
||||
inline constexpr FormatVersion OpenMW0_48SaveGameFormatVersion = 21;
|
||||
inline constexpr FormatVersion OpenMW0_49SaveGameFormatVersion = 34;
|
||||
inline constexpr FormatVersion OpenMW0_50SaveGameFormatVersion = CurrentSaveGameFormatVersion;
|
||||
inline constexpr FormatVersion OpenMW0_49MinSaveGameFormatVersion = 5;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace LuaUtil
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptsContainer::printError(int scriptId, std::string_view msg, const std::exception& e)
|
||||
void ScriptsContainer::printError(int scriptId, std::string_view msg, const std::exception& e) const
|
||||
{
|
||||
Log(Debug::Error) << mNamePrefix << "[" << scriptPath(scriptId) << "] " << msg << ": " << e.what();
|
||||
}
|
||||
|
|
@ -408,7 +408,7 @@ namespace LuaUtil
|
|||
|
||||
void ScriptsContainer::save(ESM::LuaScripts& data)
|
||||
{
|
||||
if (UnloadedData* unloadedData = std::get_if<UnloadedData>(&mData))
|
||||
if (const UnloadedData* unloadedData = std::get_if<UnloadedData>(&mData))
|
||||
{
|
||||
data.mScripts = unloadedData->mScripts;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ namespace LuaUtil
|
|||
// Returns script by id (throws an exception if doesn't exist)
|
||||
Script& getScript(int scriptId);
|
||||
|
||||
void printError(int scriptId, std::string_view msg, const std::exception& e);
|
||||
void printError(int scriptId, std::string_view msg, const std::exception& e) const;
|
||||
|
||||
const VFS::Path::Normalized& scriptPath(int scriptId) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -444,15 +444,6 @@ namespace Resource
|
|||
Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* bgsmFileManager, double expiryDelay)
|
||||
: ResourceManager(vfs, expiryDelay)
|
||||
, mShaderManager(new Shader::ShaderManager)
|
||||
, mForceShaders(false)
|
||||
, mClampLighting(true)
|
||||
, mAutoUseNormalMaps(false)
|
||||
, mAutoUseSpecularMaps(false)
|
||||
, mApplyLightingToEnvMaps(false)
|
||||
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||
, mAdjustCoverageForAlphaTest(false)
|
||||
, mSupportsNormalsRT(false)
|
||||
, mSharedStateManager(new SharedStateManager)
|
||||
, mImageManager(imageManager)
|
||||
, mNifFileManager(nifFileManager)
|
||||
|
|
@ -460,8 +451,8 @@ namespace Resource
|
|||
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
|
||||
, mMagFilter(osg::Texture::LINEAR)
|
||||
, mMaxAnisotropy(1.f)
|
||||
, mUnRefImageDataAfterApply(false)
|
||||
, mParticleSystemMask(~0u)
|
||||
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -986,8 +977,7 @@ namespace Resource
|
|||
|
||||
osg::ref_ptr<osg::Node> SceneManager::cloneErrorMarker()
|
||||
{
|
||||
if (!mErrorMarker)
|
||||
mErrorMarker = loadErrorMarker();
|
||||
std::call_once(mErrorMarkerFlag, [this] { mErrorMarker = loadErrorMarker(); });
|
||||
|
||||
return static_cast<osg::Node*>(mErrorMarker->clone(osg::CopyOp::DEEP_COPY_ALL));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,42 +237,43 @@ namespace Resource
|
|||
osg::ref_ptr<osg::Node> loadErrorMarker();
|
||||
osg::ref_ptr<osg::Node> cloneErrorMarker();
|
||||
|
||||
mutable std::mutex mSharedStateMutex;
|
||||
|
||||
std::unique_ptr<Shader::ShaderManager> mShaderManager;
|
||||
bool mForceShaders;
|
||||
bool mClampLighting;
|
||||
bool mAutoUseNormalMaps;
|
||||
std::string mNormalMapPattern;
|
||||
std::string mNormalHeightMapPattern;
|
||||
bool mAutoUseSpecularMaps;
|
||||
std::string mSpecularMapPattern;
|
||||
bool mApplyLightingToEnvMaps;
|
||||
SceneUtil::LightingMethod mLightingMethod;
|
||||
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
|
||||
bool mConvertAlphaTestToAlphaToCoverage;
|
||||
bool mAdjustCoverageForAlphaTest;
|
||||
bool mSupportsNormalsRT;
|
||||
std::array<osg::ref_ptr<osg::Texture>, 2> mOpaqueDepthTex;
|
||||
bool mWeatherParticleOcclusion = false;
|
||||
|
||||
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
|
||||
mutable std::mutex mSharedStateMutex;
|
||||
|
||||
Resource::ImageManager* mImageManager;
|
||||
Resource::NifFileManager* mNifFileManager;
|
||||
Resource::BgsmFileManager* mBgsmFileManager;
|
||||
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
|
||||
mutable osg::ref_ptr<osg::Node> mErrorMarker;
|
||||
mutable std::once_flag mErrorMarkerFlag;
|
||||
|
||||
osg::Texture::FilterMode mMinFilter;
|
||||
osg::Texture::FilterMode mMagFilter;
|
||||
float mMaxAnisotropy;
|
||||
bool mUnRefImageDataAfterApply;
|
||||
|
||||
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
|
||||
|
||||
unsigned int mParticleSystemMask;
|
||||
mutable osg::ref_ptr<osg::Node> mErrorMarker;
|
||||
SceneUtil::LightingMethod mLightingMethod;
|
||||
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
|
||||
bool mForceShaders = false;
|
||||
bool mClampLighting = true;
|
||||
bool mAutoUseNormalMaps = false;
|
||||
bool mAutoUseSpecularMaps = false;
|
||||
bool mApplyLightingToEnvMaps = false;
|
||||
bool mConvertAlphaTestToAlphaToCoverage = false;
|
||||
bool mAdjustCoverageForAlphaTest = false;
|
||||
bool mSupportsNormalsRT = false;
|
||||
bool mWeatherParticleOcclusion = false;
|
||||
bool mUnRefImageDataAfterApply = false;
|
||||
|
||||
SceneManager(const SceneManager&);
|
||||
void operator=(const SceneManager&);
|
||||
SceneManager(const SceneManager&) = delete;
|
||||
void operator=(const SceneManager&) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,31 @@ Screenshots
|
|||
.. note::
|
||||
Flatpak sets ``$XDG_DATA_HOME`` to ``$HOME/.var/app/$FLATPAK_ID/data``, so screenshots will be at ``$HOME/.var/app/org.openmw.OpenMW/data/openmw/screenshots`` if you use the Flatpak.
|
||||
|
||||
Override directory (data-local)
|
||||
-------------------------------
|
||||
|
||||
This is the directory in which OpenMW-CS saves generated content files.
|
||||
Additionally, this is always the last-loaded data directory in OpenMW, overriding any which came before it.
|
||||
This can be useful, for instance, for placing automatically-generated plugins created by external tools or to be very certain that particular assets are always overridden regardless of load order.
|
||||
You may define your own, custom ``data-local`` directory by using it as a key in ``openmw.cfg``, e.g. ``data-local=C:/Games/OpenMW/data``.
|
||||
|
||||
+--------------+----------------------------------------------------------------------------------------------------+
|
||||
| OS | Location |
|
||||
+==============+====================================================================================================+
|
||||
| Linux | ``$XDG_DATA_HOME/openmw/data`` or ``$HOME/.local/share/openmw/data`` |
|
||||
+--------------+----------------------------------------------------------------------------------------------------+
|
||||
| Mac | ``$HOME/Library/Application\ Support/openmw/data`` |
|
||||
+--------------+---------------+------------------------------------------------------------------------------------+
|
||||
| Windows | File Explorer | ``Documents\My Games\OpenMW\data`` |
|
||||
| +---------------+------------------------------------------------------------------------------------+
|
||||
| | PowerShell | ``Join-Path ([environment]::GetFolderPath("mydocuments")) "My Games\OpenMW\data"`` |
|
||||
| +---------------+------------------------------------------------------------------------------------+
|
||||
| | Example | ``C:\Users\Username\Documents\My Games\OpenMW\data`` |
|
||||
+--------------+---------------+------------------------------------------------------------------------------------+
|
||||
|
||||
.. note::
|
||||
Flatpak sets ``$XDG_DATA_HOME`` to ``$HOME/.var/app/$FLATPAK_ID/data``, so data-local will be set to ``$HOME/.var/app/org.openmw.OpenMW/data/openmw/data`` if you use the Flatpak.
|
||||
|
||||
Custom configuration directories
|
||||
================================
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,26 @@
|
|||
Ask: "sAsk"
|
||||
Back: "sBack"
|
||||
Barter: "sBarter"
|
||||
Buy: "sBuy"
|
||||
Cancel: "sCancel"
|
||||
Center: "sCenter" # This has a trailing space in Russian and French games
|
||||
Close: "sClose"
|
||||
Container: "sContainer" # This has a trailing space in the Russian game
|
||||
Create: "sCreate"
|
||||
Delete: "sDelete"
|
||||
DisposeOfCorpse: "sDisposeofCorpse"
|
||||
Done: "sDone"
|
||||
Drop: "sDrop" # This has a trailing space in the Russian game
|
||||
Equip: "sEquip" # This has a trailing space in the Russian game
|
||||
Face: "sFace"
|
||||
Goodbye: "sGoodbye"
|
||||
Hair: "sHair"
|
||||
Info: "sInfo"
|
||||
Inventory: "sInventory"
|
||||
Item: "sItem"
|
||||
Local: "sLocal"
|
||||
MagicEffects: "sMagicEffects"
|
||||
# NB: sMouse exists but it is not localized in the Russian game and should not be used to translate Mouse
|
||||
# Mouse/Move: sMouse and sMove exist but they are not localised in the Russian game and should not be used
|
||||
Next: "sNext"
|
||||
No: "sNo"
|
||||
None: "sNone"
|
||||
|
|
@ -25,6 +34,9 @@ Rest: "sRest"
|
|||
ScrollDown: "sScrolldown"
|
||||
ScrollUp: "sScrollup"
|
||||
Select: "sSelect"
|
||||
Sell: "sSell"
|
||||
Sex: "sSex"
|
||||
Share: "sCompanionShare"
|
||||
Soul: "sSoulGem"
|
||||
Take: "sTake"
|
||||
TakeAll: "sTakeAll"
|
||||
|
|
@ -32,4 +44,5 @@ Topics: "sTopics"
|
|||
Travel: "sTravel"
|
||||
UntilHealed: "sUntilHealed"
|
||||
Wait: "sWait"
|
||||
World: "sWorld"
|
||||
Yes: "sYes"
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ set(BUILTIN_DATA_FILES
|
|||
l10n/OMWCamera/fr.yaml
|
||||
l10n/OMWCamera/pl.yaml
|
||||
|
||||
l10n/OMWCombat/de.yaml
|
||||
l10n/OMWCombat/en.yaml
|
||||
l10n/OMWCombat/fr.yaml
|
||||
l10n/OMWCombat/ru.yaml
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Fragen"
|
||||
Back: "Zurück"
|
||||
Barter: "Handeln"
|
||||
Buy: "Kaufen"
|
||||
Cancel: "Abbruch"
|
||||
Center: "Zentrieren"
|
||||
Close: "Schließen"
|
||||
Container: "Behälter"
|
||||
Copy: "Kopieren"
|
||||
Create: "Herstellen"
|
||||
Delete: "Entfernen"
|
||||
DisposeOfCorpse: "Leiche beseitigen"
|
||||
Done: "Fertig"
|
||||
Drop: "Ablegen"
|
||||
DurationDay: "{days} d "
|
||||
DurationHour: "{hours} h "
|
||||
DurationMinute: "{minutes} min "
|
||||
|
|
@ -26,12 +31,17 @@ DurationYear: |-
|
|||
one{{years} Jahr }
|
||||
other{{years} Jahre }
|
||||
}
|
||||
Equip: "Verwenden"
|
||||
Face: "Gesicht"
|
||||
Goodbye: "Lebt wohl!"
|
||||
Hair: "Haar"
|
||||
Info: "Info"
|
||||
Inventory: "Inventar"
|
||||
Item: "Gegenstand"
|
||||
Local: "Lokal"
|
||||
MagicEffects: "Magischer Effekt"
|
||||
Mouse: "Maus"
|
||||
Move: "Bewegen"
|
||||
Next: "Weiter"
|
||||
No: "Nein"
|
||||
# This one is a bit tricky since it can be translated to
|
||||
|
|
@ -51,6 +61,9 @@ Rest: "Rasten"
|
|||
ScrollDown: "Nach unten scrollen"
|
||||
ScrollUp: "Nach oben scrollen"
|
||||
Select: "Auswählen"
|
||||
Sell: "Verkaufen"
|
||||
Sex: "Geschlecht"
|
||||
Share: "Teilen"
|
||||
Soul: "Seele"
|
||||
Take: "Nehmen"
|
||||
TakeAll: "Alles nehmen"
|
||||
|
|
@ -58,4 +71,5 @@ Topics: "Themen"
|
|||
Travel: "Reisen"
|
||||
UntilHealed: "Bis geheilt"
|
||||
Wait: "Warten"
|
||||
World: "Welt"
|
||||
Yes: "Ja"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Ask"
|
||||
Back: "Back"
|
||||
Barter: "Barter"
|
||||
Buy: "Buy"
|
||||
Cancel: "Cancel"
|
||||
Center: "Center"
|
||||
Close: "Close"
|
||||
Container: "Container"
|
||||
Copy: "Copy"
|
||||
Create: "Create"
|
||||
Delete: "Delete"
|
||||
DisposeOfCorpse: "Dispose of Corpse"
|
||||
Done: "Done"
|
||||
Drop: "Drop"
|
||||
DurationDay: "{days} d "
|
||||
DurationHour: "{hours} h "
|
||||
DurationMinute: "{minutes} min "
|
||||
|
|
@ -21,12 +26,17 @@ DurationYear: |-
|
|||
one{{years} yr }
|
||||
other{{years} yrs }
|
||||
}
|
||||
Equip: "Equip"
|
||||
Face: "Face"
|
||||
Goodbye: "Goodbye"
|
||||
Hair: "Hair"
|
||||
Info: "Info"
|
||||
Inventory: "Inventory"
|
||||
Item: "Item"
|
||||
Local: "Local"
|
||||
MagicEffects: "Magic Effects"
|
||||
Mouse: "Mouse"
|
||||
Move: "Move"
|
||||
Next: "Next"
|
||||
No: "No"
|
||||
None: "None"
|
||||
|
|
@ -42,6 +52,9 @@ Rest: "Rest"
|
|||
ScrollDown: "Scroll Down"
|
||||
ScrollUp: "Scroll Up"
|
||||
Select: "Select"
|
||||
Sell: "Sell"
|
||||
Sex: "Sex"
|
||||
Share: "Share"
|
||||
Soul: "Soul"
|
||||
Take: "Take"
|
||||
TakeAll: "Take All"
|
||||
|
|
@ -49,4 +62,5 @@ Topics: "Topics"
|
|||
Travel: "Travel"
|
||||
UntilHealed: "Until Healed"
|
||||
Wait: "Wait"
|
||||
World: "World"
|
||||
Yes: "Yes"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Demander"
|
||||
Back: "En arrière"
|
||||
Barter: "Marchander"
|
||||
Buy: "Acheter"
|
||||
Cancel: "Annuler"
|
||||
Center: "Centrer"
|
||||
Close: "Fermer"
|
||||
Container: "Contenant"
|
||||
Copy: "Copier"
|
||||
Create: "Créer"
|
||||
DisposeOfCorpse: "Supprimer cadavre"
|
||||
Delete: "Effacer"
|
||||
Done: "Fait"
|
||||
Drop: "Lâcher"
|
||||
DurationDay: |-
|
||||
{days, plural,
|
||||
one{{days} jour }
|
||||
|
|
@ -21,12 +26,17 @@ DurationYear: |-
|
|||
one{{years} an }
|
||||
other{{years} ans }
|
||||
}
|
||||
Equip: "S'équiper"
|
||||
Face: "Face"
|
||||
Goodbye: "Au revoir"
|
||||
Hair: "Cheveux"
|
||||
Info: "Info"
|
||||
Inventory: "Inventaire"
|
||||
Item: "Objet"
|
||||
Local: "Local"
|
||||
MagicEffects: "Effets magiques"
|
||||
Mouse: "Souris"
|
||||
Move: "Déplacement"
|
||||
Next: "Suivant"
|
||||
No: "Non"
|
||||
None: "Aucun"
|
||||
|
|
@ -42,6 +52,9 @@ Rest: "Repos"
|
|||
ScrollDown: "Défilement bas"
|
||||
ScrollUp: "Défilement haut"
|
||||
Select: "Sélectionner"
|
||||
Sell: "Vendre"
|
||||
Sex: "Sexe"
|
||||
Share: "Répartir"
|
||||
Soul: "Ame"
|
||||
Take: "Prendre"
|
||||
TakeAll: "Tout prendre"
|
||||
|
|
@ -49,4 +62,5 @@ Topics: "Sujets"
|
|||
Travel: "Voyager"
|
||||
UntilHealed: "Récup. totale"
|
||||
Wait: "Attendre"
|
||||
World: "Monde"
|
||||
Yes: "Oui"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Zapytaj"
|
||||
Back: "Wstecz"
|
||||
Barter: "Handel"
|
||||
Buy: "Kup"
|
||||
Cancel: "Anuluj"
|
||||
Center: "Centruj"
|
||||
Close: "Zamknij"
|
||||
Container: "Pojemnik"
|
||||
Copy: "Kopiuj"
|
||||
Create: "Utwórz"
|
||||
Delete: "Usuń"
|
||||
DisposeOfCorpse: "Usuń zwłoki"
|
||||
Done: "Koniec"
|
||||
Drop: "Upuść"
|
||||
DurationDay: "{days} d. "
|
||||
DurationHour: "{hours} godz. "
|
||||
DurationMinute: "{minutes} min "
|
||||
|
|
@ -19,12 +24,17 @@ DurationYear: |-
|
|||
few{{years} lata }
|
||||
many{{years} lat }
|
||||
}
|
||||
Equip: "Załóż"
|
||||
Face: "Twarz"
|
||||
Goodbye: "Do widzenia"
|
||||
Hair: "Włosy"
|
||||
Info: "Info"
|
||||
Inventory: "Ekwipunek"
|
||||
Item: "Przedmiot"
|
||||
Local: "Okolica"
|
||||
MagicEffects: "Magiczne efekty"
|
||||
Mouse: "Mysz"
|
||||
Move: "Przenieś"
|
||||
Next: "Nast."
|
||||
No: "Nie"
|
||||
None: "Brak"
|
||||
|
|
@ -40,6 +50,9 @@ Rest: "Odpocznij"
|
|||
ScrollDown: "Przewiń w dół"
|
||||
ScrollUp: "Przewiń w górę"
|
||||
Select: "Wybierz"
|
||||
Sell: "Sprzedaj"
|
||||
Sex: "Płeć"
|
||||
Share: "Podział"
|
||||
Soul: "Dusza"
|
||||
Take: "Weź"
|
||||
TakeAll: "Weź wszystko"
|
||||
|
|
@ -47,4 +60,5 @@ Topics: "Tematy"
|
|||
Travel: "Podróż"
|
||||
UntilHealed: "Do wyzdr."
|
||||
Wait: "Czekaj"
|
||||
World: "Świat"
|
||||
Yes: "Tak"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Спросить"
|
||||
Back: "Назад"
|
||||
Barter: "Торговать"
|
||||
Buy: "Купить"
|
||||
Cancel: "Отмена"
|
||||
Center: "Центр"
|
||||
Close: "Закрыть"
|
||||
Container: "Контейнер"
|
||||
Copy: "Скопировать"
|
||||
Create: "Создать"
|
||||
Delete: "Удалить"
|
||||
DisposeOfCorpse: "Убрать тело"
|
||||
Done: "Готово"
|
||||
Drop: "Бросить"
|
||||
DurationDay: "{days} д "
|
||||
DurationHour: "{hours} ч "
|
||||
DurationMinute: "{minutes} мин "
|
||||
|
|
@ -18,12 +23,17 @@ DurationYear: |-
|
|||
few{{years} г }
|
||||
other{{years} л }
|
||||
}
|
||||
Equip: "Надеть"
|
||||
Face: "Лицо"
|
||||
Goodbye: "Прощание"
|
||||
Hair: "Прическа"
|
||||
Info: "Инфо"
|
||||
Inventory: "Инвентарь"
|
||||
Item: "Предмет"
|
||||
Local: "Местность"
|
||||
MagicEffects: "Маг. эффекты"
|
||||
Mouse: "Мышь"
|
||||
Move: "Переместить"
|
||||
Next: "След"
|
||||
No: "Нет"
|
||||
None: "Нет"
|
||||
|
|
@ -39,6 +49,9 @@ Rest: "Отдых"
|
|||
ScrollDown: "Прокрутить вниз"
|
||||
ScrollUp: "Прокрутить вверх"
|
||||
Select: "Выбрать"
|
||||
Sell: "Продать"
|
||||
Sex: "Пол"
|
||||
Share: "Доля"
|
||||
Soul: "Душа"
|
||||
Take: "Взять"
|
||||
TakeAll: "Взять все"
|
||||
|
|
@ -46,4 +59,5 @@ Topics: "Темы"
|
|||
Travel: "Путешествие"
|
||||
UntilHealed: "Выздороветь"
|
||||
Wait: "Ждать"
|
||||
World: "Мир"
|
||||
Yes: "Да"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
Ask: "Fråga"
|
||||
Back: "Bakåt"
|
||||
Barter: "Handla"
|
||||
Buy: "Köp"
|
||||
Cancel: "Avbryt"
|
||||
Center: "Centrera"
|
||||
Close: "Stäng"
|
||||
Container: "Behållare"
|
||||
Copy: "Kopiera"
|
||||
Create: "Skapa"
|
||||
Delete: "Radera"
|
||||
DisposeOfCorpse: "Undanröj liket"
|
||||
Done: "Klar"
|
||||
Drop: "Släpp"
|
||||
DurationDay: "{days} d "
|
||||
DurationHour: "{hours} tim "
|
||||
DurationMinute: "{minutes} min "
|
||||
|
|
@ -21,12 +26,17 @@ DurationYear: |-
|
|||
one{{years} år }
|
||||
other{{years} år }
|
||||
}
|
||||
Equip: "Utrusta"
|
||||
Face: "Ansikte"
|
||||
Goodbye: "Adjö"
|
||||
Hair: "Hår"
|
||||
Info: "Info"
|
||||
Item: "Föremål"
|
||||
Inventory: "Inventariet"
|
||||
Local: "Lokal"
|
||||
MagicEffects: "Magiska effekter"
|
||||
Mouse: "Mus"
|
||||
Move: "Flytta"
|
||||
Next: "Nästa"
|
||||
No: "Nej"
|
||||
None: "Inget"
|
||||
|
|
@ -42,6 +52,9 @@ Rest: "Vila"
|
|||
ScrollDown: "Scrolla ner"
|
||||
ScrollUp: "Scrolla upp"
|
||||
Select: "Välj"
|
||||
Sell: "Sälj"
|
||||
Sex: "Kön"
|
||||
Share: "Dela"
|
||||
Soul: "Själ"
|
||||
Take: "Ta"
|
||||
TakeAll: "Ta allt"
|
||||
|
|
@ -49,4 +62,5 @@ Topics: "Ämnen"
|
|||
Travel: "Res"
|
||||
UntilHealed: "Tills återställd"
|
||||
Wait: "Vänta"
|
||||
World: "Värld"
|
||||
Yes: "Ja"
|
||||
|
|
|
|||
16
files/data/l10n/OMWCombat/de.yaml
Normal file
16
files/data/l10n/OMWCombat/de.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Combat: "OpenMW: Kampf"
|
||||
combatSettingsPageDescription: "OpenMW-Kampfeinstellungen"
|
||||
|
||||
combatSettings: "Kampf"
|
||||
|
||||
unarmedCreatureAttacksDamageArmor: "Angriffe unbewaffneter Kreaturen beschädigen Rüstung"
|
||||
unarmedCreatureAttacksDamageArmorDescription: |
|
||||
Auch Angriffe unbewaffneter Kreaturen beschädigen Rüstung.
|
||||
|
||||
redistributeShieldHitsWhenNotWearingShield: "Schildtreffer bei fehlendem Schild umverteilen"
|
||||
redistributeShieldHitsWhenNotWearingShieldDescription: |
|
||||
Entspricht "Shield hit location fix" aus dem Morrowind Code Patch. Wenn kein Schild getragen wird, werden Treffer auf den Schild‑Slot auf den linken Schulterpanzer oder den Kürass umverteilt.
|
||||
|
||||
spawnBloodEffectsOnPlayer: "Bluteffekte für Spieler anzeigen"
|
||||
spawnBloodEffectsOnPlayerDescription: |
|
||||
Wenn aktiviert, werden beim Spieler bei Treffern im Kampf Bluteffekte angezeigt, genau wie bei anderen Charakteren.
|
||||
|
|
@ -110,5 +110,38 @@ function aux_util.mapFilterSort(array, scoreFn)
|
|||
return sortedValues, sortedScores
|
||||
end
|
||||
|
||||
---
|
||||
-- Iterates over an array of event handlers, calling each in turn until one returns false.
|
||||
-- @function [parent=#util] callEventHandlers
|
||||
-- @param #table handlers An optional array of handlers to invoke
|
||||
-- @param #any ... Arguments to pass to each event handler
|
||||
-- @return boolean True if no further handlers should be called
|
||||
function aux_util.callEventHandlers(handlers, ...)
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](...) == false then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---
|
||||
-- Iterates over an array of event handler arrays, passing each to `aux_util.callEventHandlers` until the event is handled.
|
||||
-- @function [parent=#util] callMultipleEventHandlers
|
||||
-- @param #table handlers An array of event handler arrays
|
||||
-- @param #any ... Arguments to pass to each event handler
|
||||
-- @return boolean True if no further handlers should be called
|
||||
function aux_util.callMultipleEventHandlers(handlers, ...)
|
||||
for i = 1, #handlers do
|
||||
local stop = aux_util.callEventHandlers(handlers[i], ...)
|
||||
if stop then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return aux_util
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ local async = require('openmw.async')
|
|||
local core = require('openmw.core')
|
||||
local types = require('openmw.types')
|
||||
local world = require('openmw.world')
|
||||
local auxUtil = require('openmw_aux.util')
|
||||
|
||||
local EnableObject = async:registerTimerCallback('EnableObject', function(obj) obj.enabled = true end)
|
||||
|
||||
|
|
@ -38,21 +39,9 @@ local function onActivate(obj, actor)
|
|||
if obj.parentContainer then
|
||||
return
|
||||
end
|
||||
local handlers = handlersPerObject[obj.id]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
end
|
||||
handlers = handlersPerType[obj.type]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
local handled = auxUtil.callMultipleEventHandlers({ handlersPerObject[obj.id], handlersPerType[obj.type] }, obj, actor)
|
||||
if handled then
|
||||
return
|
||||
end
|
||||
types.Actor.activeEffects(actor):remove('invisibility')
|
||||
world._runStandardActivationAction(obj, actor)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ local self = require('openmw.self')
|
|||
local storage = require('openmw.storage')
|
||||
local types = require('openmw.types')
|
||||
local util = require('openmw.util')
|
||||
local auxUtil = require('openmw_aux.util')
|
||||
local Actor = types.Actor
|
||||
local Weapon = types.Weapon
|
||||
local Player = types.Player
|
||||
|
|
@ -270,10 +271,8 @@ local function spawnBloodEffect(position)
|
|||
end
|
||||
|
||||
local function onHit(data)
|
||||
for i = #onHitHandlers, 1, -1 do
|
||||
if onHitHandlers[i](data) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
if auxUtil.callEventHandlers(onHitHandlers, data) then
|
||||
return
|
||||
end
|
||||
if data.successful and not godMode() then
|
||||
I.Combat.applyArmor(data)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
local anim = require('openmw.animation')
|
||||
local self = require('openmw.self')
|
||||
local auxUtil = require('openmw_aux.util')
|
||||
|
||||
local playBlendedHandlers = {}
|
||||
local function onPlayBlendedAnimation(groupname, options)
|
||||
for i = #playBlendedHandlers, 1, -1 do
|
||||
if playBlendedHandlers[i](groupname, options) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
local function onPlayBlendedAnimation(groupname, options)
|
||||
auxUtil.callEventHandlers(playBlendedHandlers, groupname, options)
|
||||
end
|
||||
|
||||
local function playBlendedAnimation(groupname, options)
|
||||
|
|
@ -20,22 +17,7 @@ end
|
|||
|
||||
local textKeyHandlers = {}
|
||||
local function onAnimationTextKey(groupname, key)
|
||||
local handlers = textKeyHandlers[groupname]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](groupname, key) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
handlers = textKeyHandlers['']
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](groupname, key) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
auxUtil.callMultipleEventHandlers({ textKeyHandlers[groupname], textKeyHandlers[''] }, groupname, key)
|
||||
end
|
||||
|
||||
local initialized = false
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ local self = require('openmw.self')
|
|||
local I = require('openmw.interfaces')
|
||||
local types = require('openmw.types')
|
||||
local core = require('openmw.core')
|
||||
local auxUtil = require('openmw_aux.util')
|
||||
local NPC = require('openmw.types').NPC
|
||||
local Skill = core.stats.Skill
|
||||
|
||||
|
|
@ -104,11 +105,7 @@ local function skillUsed(skillid, options)
|
|||
end
|
||||
end
|
||||
|
||||
for i = #skillUsedHandlers, 1, -1 do
|
||||
if skillUsedHandlers[i](skillid, options) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
auxUtil.callEventHandlers(skillUsedHandlers, skillid, options)
|
||||
end
|
||||
|
||||
local function skillLevelUp(skillid, source)
|
||||
|
|
@ -144,11 +141,7 @@ local function skillLevelUp(skillid, source)
|
|||
options.levelUpSpecializationIncreaseValue = core.getGMST('iLevelupSpecialization')
|
||||
end
|
||||
|
||||
for i = #skillLevelUpHandlers, 1, -1 do
|
||||
if skillLevelUpHandlers[i](skillid, source, options) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
auxUtil.callEventHandlers(skillLevelUpHandlers, skillid, source, options)
|
||||
end
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,15 @@
|
|||
local types = require('openmw.types')
|
||||
local world = require('openmw.world')
|
||||
local auxUtil = require('openmw_aux.util')
|
||||
|
||||
local handlersPerObject = {}
|
||||
local handlersPerType = {}
|
||||
|
||||
local function useItem(obj, actor, force)
|
||||
local options = { force = force or false }
|
||||
local handlers = handlersPerObject[obj.id]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor, options) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
end
|
||||
handlers = handlersPerType[obj.type]
|
||||
if handlers then
|
||||
for i = #handlers, 1, -1 do
|
||||
if handlers[i](obj, actor, options) == false then
|
||||
return -- skip other handlers
|
||||
end
|
||||
end
|
||||
local handled = auxUtil.callMultipleEventHandlers({ handlersPerObject[obj.id], handlersPerType[obj.type] }, obj, actor, options)
|
||||
if handled then
|
||||
return
|
||||
end
|
||||
world._runStandardUseAction(obj, actor, options.force)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1224,6 +1224,16 @@
|
|||
-- @usage -- Start a new quest, add it to the player's quest list but don't add any journal entries
|
||||
-- types.Player.quests(player)["ms_fargothring"].stage = 0
|
||||
|
||||
---
|
||||
-- Adds a topic to the list of ones known by the player, so that it can be used in dialogue with actors who can talk about that topic.
|
||||
-- @function [parent=#PLAYER] addTopic
|
||||
-- @param openmw.core#GameObject player
|
||||
-- @param string topicId
|
||||
-- @usage -- Add topic to the list of known ones, in a player script
|
||||
-- self.type.addTopic(self, "Some Work")
|
||||
-- @usage -- Give all players in the current world a specific topic, in a global script
|
||||
-- for _, player in ipairs(world.players) do player.type.addTopic(player, "Some Unrelated Work") end
|
||||
|
||||
---
|
||||
-- Returns @{#PlayerJournal}, which contains the read-only access to journal text data accumulated by the player.
|
||||
-- Not the same as @{openmw_core#Dialogue.journal} which holds raw game records: with placeholders for dynamic variables and no player-specific info.
|
||||
|
|
|
|||
Loading…
Reference in a new issue