1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-10-14 15:56:34 +00:00
This commit is contained in:
Andrew Lanzone 2025-08-04 16:44:12 -07:00
commit 8e76a0ab06
146 changed files with 928 additions and 499 deletions

View file

@ -108,8 +108,8 @@ Coverity:
- ccache/
variables:
CCACHE_SIZE: 2G
CC: clang-12
CXX: clang++-12
CC: clang
CXX: clang++
CMAKE_BUILD_TYPE: Debug
CMAKE_CXX_FLAGS_DEBUG: -O0
before_script:
@ -125,18 +125,34 @@ Coverity:
- ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.linux.sh
- cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache
# Remove the specific targets and build everything once we can do it under 3h
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc)
- ccache -svv
after_script:
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA"
--form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
- echo "OPENMW_JOB_ID=$CI_JOB_ID" >> build.env
artifacts:
expire_in: 1 day
paths:
- /builds/OpenMW/openmw/cov-int/build-log.txt
- /builds/OpenMW/openmw/cov-int.tar.gz
reports:
dotenv: build.env
Coverity_Upload:
image: ubuntu:24.04
stage: build
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
before_script:
- CI/install_debian_deps.sh coverity_upload
script:
- echo "$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz"
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA"
--form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
--form url="$CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/$OPENMW_JOB_ID/artifacts/cov-int.tar.gz"
needs:
- Coverity
Ubuntu_GCC:
extends: .Ubuntu

View file

@ -11,7 +11,8 @@ print_help() {
declare -rA GROUPED_DEPS=(
[gcc]="binutils gcc build-essential cmake ccache curl unzip git pkg-config mold"
[clang]="binutils clang make cmake ccache curl unzip git pkg-config mold"
[coverity]="binutils clang-12 make cmake ccache curl unzip git pkg-config"
[coverity]="binutils clang make cmake ccache curl unzip git pkg-config file"
[coverity_upload]="curl"
[gcc_preprocess]="
binutils
build-essential

View file

@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 50)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 85)
set(OPENMW_LUA_API_REVISION 87)
set(OPENMW_POSTPROCESSING_API_REVISION 3)
set(OPENMW_VERSION_COMMITHASH "")

View file

@ -638,8 +638,9 @@ CUSTOM: customdata.lua
sol::object deserialized = LuaUtil::deserialize(lua.sol(), data2.mScripts[0].mData, &serializer1);
EXPECT_TRUE(deserialized.is<sol::table>());
sol::table table = deserialized;
for (const auto& [key, value] : table)
if (!table.empty())
{
const auto [key, value] = *table.cbegin();
EXPECT_TRUE(key.is<ESM::RefNum>());
EXPECT_TRUE(value.is<ESM::RefNum>());
EXPECT_EQ(key.as<ESM::RefNum>(), (ESM::RefNum{ 42, 34 }));

View file

@ -215,8 +215,6 @@ int main(int argc, char** argv)
std::cerr << "ERROR: " << e.what() << std::endl;
return 1;
}
return 0;
}
namespace

View file

@ -291,6 +291,7 @@ bool Launcher::SettingsPage::loadSettings()
}
}
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
dopplerSpinBox->setValue(Settings::sound().mDopplerFactor);
}
// Interface Changes
@ -485,6 +486,8 @@ void Launcher::SettingsPage::saveSettings()
const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked;
Settings::sound().mCameraListener.set(cCameraListener);
Settings::sound().mDopplerFactor.set(dopplerSpinBox->value());
}
// Interface Changes

View file

@ -1224,6 +1224,51 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="dopplerLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls the strength of the Doppler effect. Zero means it is completely disabled.&lt;/p&gt;&lt;p&gt;The Doppler effect increases or decreases the pitch of sounds relative to the velocity of the sound source and the listener.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Doppler Factor</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="dopplerSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>283</width>
<height>0</height>
</size>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.250000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer>
<property name="orientation">

View file

@ -59,14 +59,13 @@ namespace CSMWorld
const Record<ESM::LandTexture>* IdCollection<ESM::LandTexture>::searchRecord(std::uint16_t index, int plugin) const
{
auto found = mIndices.find({ plugin, index });
if (found != mIndices.end())
{
int index = searchId(found->second);
if (index != -1)
return &getRecord(index);
}
return nullptr;
const auto it = mIndices.find({ plugin, index });
if (it == mIndices.end())
return nullptr;
const int recordIndex = searchId(it->second);
if (recordIndex == -1)
return nullptr;
return &getRecord(recordIndex);
}
const std::string* IdCollection<ESM::LandTexture>::getLandTexture(std::uint16_t index, int plugin) const

View file

@ -5,7 +5,8 @@
#include <future>
#include <system_error>
#include <osgDB/WriteFile>
#include <osgDB/ReaderWriter>
#include <osgDB/Registry>
#include <osgViewer/ViewerEventHandlers>
#include <SDL.h>
@ -28,7 +29,6 @@
#include <components/compiler/extensions0.hpp>
#include <components/stereo/multiview.hpp>
#include <components/stereo/stereomanager.hpp>
#include <components/sceneutil/glextensions.hpp>

View file

@ -75,6 +75,7 @@ namespace MWBase
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
= 0;
virtual void jailTimeServed(const MWWorld::Ptr& actor, int days) = 0;
virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
virtual void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon,

View file

@ -234,6 +234,8 @@ namespace MWBase
const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater)
= 0;
virtual void setListenerVel(const osg::Vec3f& vel) = 0;
virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0;
void setSimulationTimeScale(float scale) { mSimulationTimeScale = scale; }

View file

@ -496,6 +496,8 @@ namespace MWBase
virtual float getSunVisibility() const = 0;
virtual float getSunPercentage() const = 0;
virtual float getPhysicsFrameRateDt() const = 0;
virtual bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) = 0;
/// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker)

View file

@ -4,6 +4,7 @@
#include <components/misc/strings/format.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/luamanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
@ -86,46 +87,6 @@ namespace MWGui
// We should not worsen corprus when in prison
player.getClass().getCreatureStats(player).getActiveSpells().skipWorsenings(mDays * 24);
const auto& skillStore = MWBase::Environment::get().getESMStore()->get<ESM::Skill>();
std::set<const ESM::Skill*> skills;
for (int day = 0; day < mDays; ++day)
{
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
const ESM::Skill* skill = skillStore.searchRandom({}, prng);
skills.insert(skill);
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId);
if (skill->mId == ESM::Skill::Security || skill->mId == ESM::Skill::Sneak)
value.setBase(std::min(100.f, value.getBase() + 1));
else
value.setBase(std::max(0.f, value.getBase() - 1));
}
const MWWorld::Store<ESM::GameSetting>& gmst
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
std::string message;
if (mDays == 1)
message = gmst.find("sNotifyMessage42")->mValue.getString();
else
message = gmst.find("sNotifyMessage43")->mValue.getString();
message = Misc::StringUtils::format(message, mDays);
for (const ESM::Skill* skill : skills)
{
int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mId).getBase();
std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString();
if (skill->mId == ESM::Skill::Sneak || skill->mId == ESM::Skill::Security)
skillMsg = gmst.find("sNotifyMessage39")->mValue.getString();
skillMsg = Misc::StringUtils::format(skillMsg, skill->mName, skillValue);
message += "\n" + skillMsg;
}
std::vector<std::string> buttons;
buttons.emplace_back("#{Interface:OK}");
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons);
MWBase::Environment::get().getLuaManager()->jailTimeServed(player, mDays);
}
}

View file

@ -775,12 +775,10 @@ namespace MWGui
, mGlobalMapRender(std::make_unique<MWRender::GlobalMap>(localMapRender->getRoot(), workQueue))
, mEditNoteDialog()
{
static bool registered = false;
if (!registered)
{
[[maybe_unused]] static const bool registered = [] {
MyGUI::FactoryManager::getInstance().registerFactory<MarkerWidget>("Widget");
registered = true;
}
return true;
}();
mEditNoteDialog.setVisible(false);
mEditNoteDialog.eventOkClicked += MyGUI::newDelegate(this, &MapWindow::onNoteEditOk);

View file

@ -587,6 +587,7 @@ namespace MWGui
getWidget(mAvailableEffectsList, "AvailableEffects");
getWidget(mUsedEffectsView, "UsedEffects");
getWidget(mPriceLabel, "PriceLabel");
getWidget(mPlayerGold, "PlayerGold");
getWidget(mBuyButton, "BuyButton");
getWidget(mCancelButton, "CancelButton");
@ -613,6 +614,10 @@ namespace MWGui
mPtr = actor;
mNameEdit->setCaption({});
MWWorld::Ptr player = MWMechanics::getPlayer();
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
mPlayerGold->setCaptionWithReplacing(MyGUI::utility::toString(playerGold));
startEditing();
}

View file

@ -190,6 +190,7 @@ namespace MWGui
MyGUI::Button* mBuyButton;
MyGUI::Button* mCancelButton;
MyGUI::TextBox* mPriceLabel;
MyGUI::TextBox* mPlayerGold;
ESM::Spell mSpell;
};

View file

@ -29,7 +29,7 @@ namespace MWGui
: WindowBase("openmw_trainingwindow.layout")
{
getWidget(mTrainingOptions, "TrainingOptions");
getWidget(mCancelButton, "CancelButton");
getWidget(mCancelButton, "OkButton");
getWidget(mPlayerGold, "PlayerGold");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked);
@ -123,14 +123,14 @@ namespace MWGui
MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(price <= playerGold
? "SandTextButton"
: "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip
MyGUI::IntCoord(5, 5 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight),
MyGUI::IntCoord(4, 3 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight),
MyGUI::Align::Default);
button->setUserData(skills[i].first);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);
button->setCaptionWithReplacing(
MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price));
MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price) + "#{sgp}");
button->setSize(button->getTextSize().width + 12, button->getSize().height);

View file

@ -1,13 +1,7 @@
#include "animationbindings.hpp"
#include <components/esm3/loadmgef.hpp>
#include <components/esm3/loadstat.hpp>
#include <components/lua/asyncpackage.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/utilpackage.hpp>
#include <components/misc/finitenumbers.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -15,11 +9,8 @@
#include "../mwmechanics/character.hpp"
#include "../mwworld/esmstore.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
#include "objectvariant.hpp"
namespace MWLua
{

View file

@ -8,7 +8,7 @@
#include "../mwbase/environment.hpp"
#include "idcollectionbindings.hpp"
#include "types/types.hpp"
#include "recordstore.hpp"
namespace sol
{

View file

@ -3,10 +3,10 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initBirthSignRecordBindings(const Context& context);
}

View file

@ -1,7 +1,5 @@
#include "cellbindings.hpp"
#include <components/esm/esmbridge.hpp>
#include <components/esm3/loadacti.hpp>
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadappa.hpp>
@ -27,7 +25,6 @@
#include <components/esm4/loadammo.hpp>
#include <components/esm4/loadarmo.hpp>
#include <components/esm4/loadbook.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/esm4/loadclot.hpp>
#include <components/esm4/loadcont.hpp>
#include <components/esm4/loaddoor.hpp>
@ -38,7 +35,6 @@
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadmstt.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/esm4/loadscol.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadtree.hpp>

View file

@ -1,12 +1,12 @@
#ifndef MWLUA_CELLBINDINGS_H
#define MWLUA_CELLBINDINGS_H
#include "context.hpp"
namespace MWLua
{
void initCellBindingsForLocalScripts(const Context&);
void initCellBindingsForGlobalScripts(const Context&);
struct Context;
void initCellBindingsForLocalScripts(const Context& context);
void initCellBindingsForGlobalScripts(const Context& context);
}
#endif // MWLUA_CELLBINDINGS_H

View file

@ -4,7 +4,7 @@
#include <components/lua/luastate.hpp>
#include "idcollectionbindings.hpp"
#include "types/types.hpp"
#include "recordstore.hpp"
namespace sol
{

View file

@ -3,10 +3,10 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initClassRecordBindings(const Context& context);
}

View file

@ -4,7 +4,6 @@
#include <stdexcept>
#include <components/debug/debuglog.hpp>
#include <components/esm3/loadfact.hpp>
#include <components/lua/l10n.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/serialization.hpp>
@ -20,6 +19,7 @@
#include "../mwworld/datetimemanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "context.hpp"
#include "coremwscriptbindings.hpp"
#include "dialoguebindings.hpp"
#include "factionbindings.hpp"

View file

@ -3,13 +3,13 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
void addCoreTimeBindings(sol::table& api, const Context& context);
sol::table initCorePackage(const Context&);
sol::table initCorePackage(const Context& context);
}
#endif // MWLUA_COREBINDINGS_H

View file

@ -1,7 +1,7 @@
#ifndef OPENMW_MWLUA_DEBUGBINDINGS_H
#define OPENMW_MWLUA_DEBUGBINDINGS_H
#include <sol/sol.hpp>
#include <sol/forward.hpp>
namespace MWLua
{

View file

@ -113,6 +113,15 @@ namespace MWLua
scripts->onSkillLevelUp(event.mSkill, event.mSource);
}
void operator()(const OnJailTimeServed& event) const
{
MWWorld::Ptr actor = getPtr(event.mActor);
if (actor.isEmpty())
return;
if (auto* scripts = getLocalScripts(actor))
scripts->onJailTimeServed(event.mDays);
}
private:
MWWorld::Ptr getPtr(ESM::RefNum id) const
{

View file

@ -70,8 +70,13 @@ namespace MWLua
std::string mSkill;
std::string mSource;
};
struct OnJailTimeServed
{
ESM::RefNum mActor;
int mDays;
};
using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported,
OnAnimationTextKey, OnSkillUse, OnSkillLevelUp>;
OnAnimationTextKey, OnSkillUse, OnSkillLevelUp, OnJailTimeServed>;
void clear() { mQueue.clear(); }
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }

View file

@ -3,10 +3,10 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initCoreFactionBindings(const Context& context);
}

View file

@ -14,6 +14,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwinput/actions.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
namespace sol

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initInputPackage(const Context&);
struct Context;
sol::table initInputPackage(const Context& context);
}
#endif // MWLUA_INPUTBINDINGS_H

View file

@ -1,14 +1,24 @@
#include "landbindings.hpp"
#include <span>
#include <string>
#include <sol/object.hpp>
#include <sol/table.hpp>
#include <sol/variadic_results.hpp>
#include <components/esm/refid.hpp>
#include <components/esm/util.hpp>
#include <components/esmterrain/storage.hpp>
#include <components/lua/luastate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/worldmodel.hpp"
#include "context.hpp"
#include "object.hpp"
namespace

View file

@ -1,10 +1,12 @@
#ifndef MWLUA_LANDBINDINGS_H
#define MWLUA_LANDBINDINGS_H
#include "context.hpp"
#include <sol/forward.hpp>
namespace MWLua
{
struct Context;
sol::table initCoreLandBindings(const Context& context);
}

View file

@ -1,9 +1,5 @@
#include "localscripts.hpp"
#include <components/esm3/loadcell.hpp>
#include <components/esm3/loadweap.hpp>
#include <components/misc/strings/lower.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/aicombat.hpp"
@ -232,7 +228,7 @@ namespace MWLua
[&](LuaUtil::LuaView& view) { addPackage("openmw.self", sol::make_object(view.sol(), &mData)); });
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse,
&mOnSkillLevelUp });
&mOnSkillLevelUp, &mOnJailTimeServed });
}
void LocalScripts::setActive(bool active, bool callHandlers)

View file

@ -1,9 +1,7 @@
#ifndef MWLUA_LOCALSCRIPTS_H
#define MWLUA_LOCALSCRIPTS_H
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include <components/lua/luastate.hpp>
@ -89,6 +87,7 @@ namespace MWLua
{
callEngineHandlers(mOnSkillLevelUp, skillId, source);
}
void onJailTimeServed(int days) { callEngineHandlers(mOnJailTimeServed, days); }
void applyStatsCache();
@ -118,6 +117,7 @@ namespace MWLua
EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
EngineHandlerList mOnSkillUse{ "_onSkillUse" };
EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" };
EngineHandlerList mOnJailTimeServed{ "_onJailTimeServed" };
};
}

View file

@ -1,29 +1,30 @@
#ifndef MWLUA_LUABINDINGS_H
#define MWLUA_LUABINDINGS_H
#include <map>
#include <sol/forward.hpp>
#include <string>
#include "context.hpp"
#include <map>
#include <string>
namespace MWLua
{
struct Context;
// Initialize Lua packages that are available for all scripts.
std::map<std::string, sol::object> initCommonPackages(const Context&);
std::map<std::string, sol::object> initCommonPackages(const Context& context);
// Initialize Lua packages that are available for global scripts (additionally to common packages).
std::map<std::string, sol::object> initGlobalPackages(const Context&);
std::map<std::string, sol::object> initGlobalPackages(const Context& context);
// Initialize Lua packages that are available for local scripts (additionally to common packages).
std::map<std::string, sol::object> initLocalPackages(const Context&);
std::map<std::string, sol::object> initLocalPackages(const Context& context);
// Initialize Lua packages that are available only for local scripts on the player (additionally to common and local
// packages).
std::map<std::string, sol::object> initPlayerPackages(const Context&);
std::map<std::string, sol::object> initPlayerPackages(const Context& context);
// Initialize Lua packages that are available only for menu scripts (additionally to common packages).
std::map<std::string, sol::object> initMenuPackages(const Context&);
std::map<std::string, sol::object> initMenuPackages(const Context& context);
}
#endif // MWLUA_LUABINDINGS_H

View file

@ -5,6 +5,10 @@
#include <MyGUI_InputManager.h>
#include <osg/Stats>
#include <sol/object.hpp>
#include <sol/table.hpp>
#include <sol/types.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/luascripts.hpp>
@ -15,7 +19,6 @@
#include <components/l10n/manager.hpp>
#include <components/lua_ui/content.hpp>
#include <components/lua_ui/registerscriptsettings.hpp>
#include <components/lua_ui/util.hpp>
@ -212,13 +215,12 @@ namespace MWLua
// Run engine handlers
mEngineEvents.callEngineHandlers();
if (!timeManager.isPaused())
{
float frameDuration = MWBase::Environment::get().getFrameDuration();
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->update(frameDuration);
mGlobalScripts.update(frameDuration);
}
bool isPaused = timeManager.isPaused();
float frameDuration = MWBase::Environment::get().getFrameDuration();
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->update(isPaused ? 0 : frameDuration);
mGlobalScripts.update(isPaused ? 0 : frameDuration);
mLua.protectedCall([&](LuaUtil::LuaView& lua) { mScriptTracker.unloadInactiveScripts(lua); });
}
@ -491,6 +493,11 @@ namespace MWLua
EngineEvents::OnSkillLevelUp{ getId(actor), skillId.serializeText(), std::string(source) });
}
void LuaManager::jailTimeServed(const MWWorld::Ptr& actor, int days)
{
mEngineEvents.addToQueue(EngineEvents::OnJailTimeServed{ getId(actor), days });
}
void LuaManager::onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon,
const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth,
const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType)

View file

@ -3,9 +3,10 @@
#include <filesystem>
#include <map>
#include <osg/Stats>
#include <set>
#include <osg/Stats>
#include <components/lua/inputactions.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/scripttracker.hpp>
@ -92,6 +93,7 @@ namespace MWLua
bool loopfallback) override;
void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) override;
void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) override;
void jailTimeServed(const MWWorld::Ptr& actor, int days) override;
void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon,
const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth,
const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType sourceType) override;

View file

@ -3,10 +3,10 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initCoreMagicBindings(const Context& context);
void addActorMagicBindings(sol::table& actor, const Context& context);
}

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initMarkupPackage(const Context&);
struct Context;
sol::table initMarkupPackage(const Context& context);
}
#endif // MWLUA_MARKUPBINDINGS_H

View file

@ -7,6 +7,8 @@
#include "../mwbase/statemanager.hpp"
#include "../mwstate/character.hpp"
#include "context.hpp"
namespace MWLua
{
static const MWState::Character* findCharacter(std::string_view characterDir)

View file

@ -9,11 +9,11 @@
#include "../mwbase/luamanager.hpp"
#include "context.hpp"
#include "inputprocessor.hpp"
namespace MWLua
{
struct Context;
sol::table initMenuPackage(const Context& context);

View file

@ -12,6 +12,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwworld/worldimp.hpp"
#include "context.hpp"
#include "object.hpp"
#include <stdexcept>

View file

@ -3,13 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initMWScriptBindings(const Context&);
sol::table initMWScriptBindings(const Context& context);
}
#endif // MWLUA_MWSCRIPTBINDINGS_H

View file

@ -13,9 +13,12 @@
#include "../mwworld/cellstore.hpp"
#include "../mwworld/scene.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
#include "objectlists.hpp"
#include <vector>
namespace
{
template <class T = MWWorld::Ptr>

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initNearbyPackage(const Context&);
struct Context;
sol::table initNearbyPackage(const Context& context);
}
#endif // MWLUA_NEARBYBINDINGS_H

View file

@ -1,9 +1,7 @@
#ifndef MWLUA_OBJECT_H
#define MWLUA_OBJECT_H
#include <map>
#include <stdexcept>
#include <typeindex>
#include <sol/sol.hpp>

View file

@ -1,12 +1,12 @@
#ifndef MWLUA_OBJECTBINDINGS_H
#define MWLUA_OBJECTBINDINGS_H
#include "context.hpp"
namespace MWLua
{
void initObjectBindingsForLocalScripts(const Context&);
void initObjectBindingsForGlobalScripts(const Context&);
struct Context;
void initObjectBindingsForLocalScripts(const Context& context);
void initObjectBindingsForGlobalScripts(const Context& context);
}
#endif // MWLUA_OBJECTBINDINGS_H

View file

@ -1,9 +1,5 @@
#include "objectlists.hpp"
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/environment.hpp"

View file

@ -8,6 +8,7 @@
#include "../mwbase/world.hpp"
#include "../mwrender/postprocessor.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
namespace

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initPostprocessingPackage(const Context&);
struct Context;
sol::table initPostprocessingPackage(const Context& context);
}
#endif // MWLUA_POSTPROCESSINGBINDINGS_H

View file

@ -8,7 +8,7 @@
#include "../mwworld/esmstore.hpp"
#include "idcollectionbindings.hpp"
#include "types/types.hpp"
#include "recordstore.hpp"
namespace
{

View file

@ -3,10 +3,10 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
struct Context;
sol::table initRaceRecordBindings(const Context& context);
}

View file

@ -1,10 +1,16 @@
#ifndef MWLUA_RECORDSTORE_H
#define MWLUA_RECORDSTORE_H
#include <sol/sol.hpp>
#include <type_traits>
#include <sol/forward.hpp>
#include <sol/overload.hpp>
#include <sol/state_view.hpp>
#include <sol/table.hpp>
#include <sol/types.hpp>
#include <sol/unsafe_function.hpp>
#include <sol/usertype.hpp>
#include <components/esm/defs.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/util.hpp>
#include "apps/openmw/mwbase/environment.hpp"

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initCoreSoundBindings(const Context&);
struct Context;
sol::table initCoreSoundBindings(const Context& context);
sol::table initAmbientPackage(const Context& context);
}

View file

@ -1,9 +1,11 @@
#include "stats.hpp"
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <components/esm3/loadclas.hpp>

View file

@ -108,6 +108,10 @@ namespace MWLua
}
luaManager->addUIMessage(message, mode);
};
api["_showInteractiveMessage"] = [windowManager](std::string_view message, sol::optional<sol::table>) {
windowManager->interactiveMessageBox(message, { "#{Interface:OK}" });
};
api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string, Misc::Color>(lua,
{
{ "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) },

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initUserInterfacePackage(const Context&);
struct Context;
sol::table initUserInterfacePackage(const Context& context);
}
#endif // MWLUA_UIBINDINGS_H

View file

@ -1,7 +1,8 @@
#ifndef MWLUA_USERDATASERIALIZER_H
#define MWLUA_USERDATASERIALIZER_H
#include "object.hpp"
#include <map>
#include <memory>
namespace LuaUtil
{

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initVFSPackage(const Context&);
struct Context;
sol::table initVFSPackage(const Context& context);
}
#endif // MWLUA_VFSBINDINGS_H

View file

@ -7,7 +7,6 @@
#include <components/esm3/loadclot.hpp>
#include <components/esm3/loadligh.hpp>
#include <components/esm3/loadmisc.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/esm3/loadweap.hpp>
#include <components/lua/luastate.hpp>
#include <components/misc/finitenumbers.hpp>
@ -27,6 +26,7 @@
#include "luamanagerimp.hpp"
#include "animationbindings.hpp"
#include "context.hpp"
#include "corebindings.hpp"
#include "mwscriptbindings.hpp"

View file

@ -3,11 +3,11 @@
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initWorldPackage(const Context&);
struct Context;
sol::table initWorldPackage(const Context& context);
}
#endif // MWLUA_WORLDBINDINGS_H

View file

@ -93,9 +93,10 @@ namespace
namespace MWPhysics
{
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
: mShapeManager(
std::make_unique<Resource::BulletShapeManager>(resourceSystem->getVFS(), resourceSystem->getSceneManager(),
resourceSystem->getNifFileManager(), Settings::cells().mCacheExpiryDelay))
: mPhysicsDt(1.f / 60.f)
, mShapeManager(std::make_unique<Resource::BulletShapeManager>(resourceSystem->getVFS(),
resourceSystem->getSceneManager(), resourceSystem->getNifFileManager(),
Settings::cells().mCacheExpiryDelay))
, mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false)
, mTimeAccum(0.0f)
@ -103,7 +104,6 @@ namespace MWPhysics
, mWaterHeight(0)
, mWaterEnabled(false)
, mParentNode(std::move(parentNode))
, mPhysicsDt(1.f / 60.f)
{
mResourceSystem->addResourceManager(mShapeManager.get());

View file

@ -287,6 +287,8 @@ namespace MWPhysics
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
void reportCollision(const btVector3& position, const btVector3& normal);
float mPhysicsDt;
private:
void updateWater();
@ -330,8 +332,6 @@ namespace MWPhysics
osg::ref_ptr<osg::Group> mParentNode;
float mPhysicsDt;
std::size_t mSimulationsCounter = 0;
std::array<std::vector<Simulation>, 2> mSimulations;
std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>> mActorsPositions;

View file

@ -1117,8 +1117,8 @@ namespace MWRender
return keyframeController->getAsCallback();
}
return asCallback;
else
return asCallback;
}
void Animation::resetActiveGroups()

View file

@ -401,14 +401,12 @@ namespace MWRender
{
if (mViewMode == VM_FirstPerson)
{
static bool prototypeAdded = false;
if (!prototypeAdded)
{
[[maybe_unused]] static const bool prototypeAdded = [&] {
osg::ref_ptr<osgUtil::RenderBin> depthClearBin(new osgUtil::RenderBin);
depthClearBin->setDrawCallback(new DepthClearCallback());
osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin);
prototypeAdded = true;
}
return true;
}();
mObjectRoot->getOrCreateStateSet()->setRenderBinDetails(
RenderBin_FirstPerson, "DepthClear", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
}

View file

@ -88,14 +88,13 @@ namespace MWRender
if (mProgramBlobber != nullptr)
{
static bool pipelineLogged = [&] {
[[maybe_unused]] static const bool pipelineLogged = [&] {
if (mUseCompute)
Log(Debug::Info) << "Initialized compute shader pipeline for water ripples";
else
Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples";
return true;
}();
(void)pipelineLogged;
}
setCullCallback(new osg::NodeCallback);

View file

@ -1,6 +1,8 @@
#include "terrainstorage.hpp"
#include <components/esm3/loadland.hpp>
#include <components/esm4/loadltex.hpp>
#include <components/esm4/loadtxst.hpp>
#include <components/esm4/loadwrld.hpp>
#include "../mwbase/environment.hpp"
@ -111,4 +113,15 @@ namespace MWRender
return esmStore.get<ESM::LandTexture>().search(index, plugin);
}
const ESM4::LandTexture* TerrainStorage::getEsm4LandTexture(ESM::RefId ltexId) const
{
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
return esmStore.get<ESM4::LandTexture>().search(ltexId);
}
const ESM4::TextureSet* TerrainStorage::getEsm4TextureSet(ESM::RefId txstId) const
{
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
return esmStore.get<ESM4::TextureSet>().search(txstId);
}
}

View file

@ -24,6 +24,9 @@ namespace MWRender
osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
const std::string* getLandTexture(std::uint16_t index, int plugin) override;
const ESM4::LandTexture* getEsm4LandTexture(ESM::RefId ltexId) const override;
const ESM4::TextureSet* getEsm4TextureSet(ESM::RefId txstId) const override;
bool hasData(ESM::ExteriorCellLocation cellLocation) override;
/// Get bounds of the whole terrain in cell units

View file

@ -604,7 +604,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
std::string_view effect = runtime.getStringLiteral(runtime[0].mInteger);
const std::string_view effectName = runtime.getStringLiteral(runtime[0].mInteger);
runtime.pop();
if (!ptr.getClass().isActor())
@ -615,11 +615,11 @@ namespace MWScript
long key;
if (const auto k = ::Misc::StringUtils::toNumeric<long>(effect.data());
if (const auto k = ::Misc::StringUtils::toNumeric<long>(effectName);
k.has_value() && *k >= 0 && *k <= 32767)
key = *k;
else
key = ESM::MagicEffect::effectGmstIdToIndex(effect);
key = ESM::MagicEffect::effectGmstIdToIndex(effectName);
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
for (const auto& spell : stats.getActiveSpells())

View file

@ -522,16 +522,14 @@ namespace MWSound
/* We need to make sure ffmpeg is initialized. Optionally silence warning
* output from the lib */
static bool done_init = false;
if (!done_init)
{
[[maybe_unused]] static const bool doneInit = [] {
// This is not needed anymore above FFMpeg version 4.0
#if LIBAVCODEC_VERSION_INT < 3805796
av_register_all();
#endif
av_log_set_level(AV_LOG_ERROR);
done_init = true;
}
return true;
}();
}
FFmpegDecoder::~FFmpegDecoder()

View file

@ -15,6 +15,7 @@
#include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/misc/thread.hpp>
#include <components/settings/values.hpp>
#include <components/vfs/manager.hpp>
#include "efxpresets.h"
@ -963,6 +964,7 @@ namespace MWSound
// Speed of sound is in units per second. Take the sound speed in air (assumed
// meters per second), multiply by the units per meter to get the speed in u/s.
alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter);
alDopplerFactor(Settings::sound().mDopplerFactor);
alGetError();
mInitialized = true;
@ -1142,8 +1144,8 @@ namespace MWSound
alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
}
void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist,
ALfloat gain, ALfloat pitch, bool loop, bool useenv)
void OpenALOutput::initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist,
ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv)
{
alSourcef(source, AL_REFERENCE_DISTANCE, mindist);
alSourcef(source, AL_MAX_DISTANCE, maxdist);
@ -1179,11 +1181,11 @@ namespace MWSound
alSourcef(source, AL_PITCH, pitch);
alSourcefv(source, AL_POSITION, pos.ptr());
alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcefv(source, AL_VELOCITY, vel.ptr());
}
void OpenALOutput::updateCommon(
ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv)
void OpenALOutput::updateCommon(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist,
ALfloat gain, ALfloat pitch, bool useenv)
{
if (useenv && mListenerEnv == Env_Underwater && !mWaterFilter)
{
@ -1195,7 +1197,7 @@ namespace MWSound
alSourcef(source, AL_PITCH, pitch);
alSourcefv(source, AL_POSITION, pos.ptr());
alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcefv(source, AL_VELOCITY, vel.ptr());
}
bool OpenALOutput::playSound(Sound* sound, Sound_Handle data, float offset)
@ -1248,8 +1250,9 @@ namespace MWSound
}
source = mFreeSources.front();
initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), sound->getUseEnv());
initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(),
sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(),
sound->getUseEnv());
alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcef(source, AL_SEC_OFFSET, offset);
if (getALError() != AL_NO_ERROR)
@ -1312,8 +1315,8 @@ namespace MWSound
return;
ALuint source = GET_PTRID(sound->mHandle);
updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
getTimeScaledPitch(sound), sound->getUseEnv());
updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(),
sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv());
getALError();
}
@ -1360,8 +1363,8 @@ namespace MWSound
if (sound->getIsLooping())
Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\"";
initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv());
initCommon3D(source, sound->getPosition(), sound->getVelocity(), sound->getMinDistance(),
sound->getMaxDistance(), sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv());
if (getALError() != AL_NO_ERROR)
return false;
@ -1443,8 +1446,8 @@ namespace MWSound
OpenAL_SoundStream* stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
ALuint source = stream->mSource;
updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
getTimeScaledPitch(sound), sound->getUseEnv());
updateCommon(source, sound->getPosition(), sound->getVelocity(), sound->getMaxDistance(),
sound->getRealVolume(), getTimeScaledPitch(sound), sound->getUseEnv());
getALError();
}
@ -1459,12 +1462,13 @@ namespace MWSound
}
void OpenALOutput::updateListener(
const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env)
const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, const osg::Vec3f& vel, Environment env)
{
if (mContext)
{
ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() };
alListenerfv(AL_POSITION, pos.ptr());
alListenerfv(AL_VELOCITY, vel.ptr());
alListenerfv(AL_ORIENTATION, orient);
if (env != mListenerEnv)
@ -1497,6 +1501,7 @@ namespace MWSound
}
mListenerPos = pos;
mListenerVel = vel;
mListenerEnv = env;
}
@ -1582,7 +1587,6 @@ namespace MWSound
: SoundOutput(mgr)
, mDevice(nullptr)
, mContext(nullptr)
, mListenerPos(0.0f, 0.0f, 0.0f)
, mListenerEnv(Env_Normal)
, mWaterFilter(0)
, mWaterEffect(0)

View file

@ -46,6 +46,7 @@ namespace MWSound
StreamVec mActiveStreams;
osg::Vec3f mListenerPos;
osg::Vec3f mListenerVel;
Environment mListenerEnv;
ALuint mWaterFilter;
@ -64,11 +65,11 @@ namespace MWSound
std::unique_ptr<DefaultDeviceThread> mDefaultDeviceThread;
void initCommon2D(ALuint source, const osg::Vec3f& pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv);
void initCommon3D(ALuint source, const osg::Vec3f& pos, ALfloat mindist, ALfloat maxdist, ALfloat gain,
ALfloat pitch, bool loop, bool useenv);
void initCommon3D(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat mindist, ALfloat maxdist,
ALfloat gain, ALfloat pitch, bool loop, bool useenv);
void updateCommon(
ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv);
void updateCommon(ALuint source, const osg::Vec3f& pos, const osg::Vec3f& vel, ALfloat maxdist, ALfloat gain,
ALfloat pitch, bool useenv);
float getTimeScaledPitch(SoundBase* sound);
@ -108,8 +109,8 @@ namespace MWSound
void startUpdate() override;
void finishUpdate() override;
void updateListener(
const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env) override;
void updateListener(const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir,
const osg::Vec3f& vel, Environment env) override;
void pauseSounds(int types) override;
void resumeSounds(int types) override;

View file

@ -31,6 +31,8 @@ namespace MWSound
struct SoundParams
{
osg::Vec3f mPos;
osg::Vec3f mLastPos;
osg::Vec3f mVel;
float mVolume = 1.0f;
float mBaseVolume = 1.0f;
float mPitch = 1.0f;
@ -57,6 +59,8 @@ namespace MWSound
public:
void setPosition(const osg::Vec3f& pos) { mParams.mPos = pos; }
void setLastPosition(const osg::Vec3f& lastpos) { mParams.mLastPos = lastpos; }
void setVelocity(const osg::Vec3f& vel) { mParams.mVel = vel; }
void setVolume(float volume) { mParams.mVolume = volume; }
void setBaseVolume(float volume) { mParams.mBaseVolume = volume; }
void setFadeout(float duration) { setFade(duration, 0.0, Play_StopAtFadeEnd); }
@ -150,6 +154,8 @@ namespace MWSound
}
const osg::Vec3f& getPosition() const { return mParams.mPos; }
const osg::Vec3f& getLastPosition() const { return mParams.mLastPos; }
const osg::Vec3f& getVelocity() const { return mParams.mVel; }
float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume * mParams.mFadeVolume; }
float getPitch() const { return mParams.mPitch; }
float getMinDistance() const { return mParams.mMinDistance; }

View file

@ -119,6 +119,7 @@ namespace MWSound
, mListenerPos(0, 0, 0)
, mListenerDir(1, 0, 0)
, mListenerUp(0, 0, 1)
, mListenerVel(0, 0, 0)
, mUnderwaterSound(nullptr)
, mNearWaterSound(nullptr)
, mPlaybackPaused(false)
@ -960,7 +961,7 @@ namespace MWSound
}
mOutput->startUpdate();
mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, env);
mOutput->updateListener(mListenerPos, mListenerDir, mListenerUp, mListenerVel, env);
updateMusic(duration);
@ -977,7 +978,13 @@ namespace MWSound
if (sound->getIs3D())
{
if (!ptr.isEmpty())
{
sound->setLastPosition(sound->getPosition());
sound->setPosition(ptr.getRefData().getPosition().asVec3());
MWBase::World* world = MWBase::Environment::get().getWorld();
sound->setVelocity(
(sound->getPosition() - sound->getLastPosition()) / world->getPhysicsFrameRateDt());
}
cull3DSound(sound);
}
@ -1013,8 +1020,11 @@ namespace MWSound
{
if (!ptr.isEmpty())
{
sound->setLastPosition(sound->getPosition());
MWBase::World* world = MWBase::Environment::get().getWorld();
sound->setPosition(world->getActorHeadTransform(ptr).getTrans());
sound->setVelocity(
(sound->getPosition() - sound->getLastPosition()) / world->getPhysicsFrameRateDt());
}
cull3DSound(sound);
@ -1153,6 +1163,11 @@ namespace MWSound
mWaterSoundUpdater.setUnderwater(underwater);
}
void SoundManager::setListenerVel(const osg::Vec3f& vel)
{
mListenerVel = vel;
}
void SoundManager::updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated)
{
SoundMap::iterator snditer = mActiveSounds.find(old.mRef);

View file

@ -92,6 +92,7 @@ namespace MWSound
osg::Vec3f mListenerPos;
osg::Vec3f mListenerDir;
osg::Vec3f mListenerUp;
osg::Vec3f mListenerVel;
int mPausedSoundTypes[BlockerType::MaxCount] = {};
@ -283,6 +284,8 @@ namespace MWSound
void setListenerPosDir(
const osg::Vec3f& pos, const osg::Vec3f& dir, const osg::Vec3f& up, bool underwater) override;
void setListenerVel(const osg::Vec3f& vel) override;
void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) override;
void clear() override;

View file

@ -61,8 +61,8 @@ namespace MWSound
virtual void startUpdate() = 0;
virtual void finishUpdate() = 0;
virtual void updateListener(
const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir, Environment env)
virtual void updateListener(const osg::Vec3f& pos, const osg::Vec3f& atdir, const osg::Vec3f& updir,
const osg::Vec3f& vel, Environment env)
= 0;
virtual void pauseSounds(int types) = 0;

View file

@ -110,6 +110,7 @@ namespace ESM4
struct Static;
struct StaticCollection;
struct Terminal;
struct TextureSet;
struct Tree;
struct Weapon;
struct World;
@ -149,7 +150,7 @@ namespace MWWorld
Store<ESM4::LevelledNpc>, Store<ESM4::Light>, Store<ESM4::MiscItem>, Store<ESM4::MovableStatic>,
Store<ESM4::Npc>, Store<ESM4::Outfit>, Store<ESM4::Potion>, Store<ESM4::Race>, Store<ESM4::Reference>,
Store<ESM4::Sound>, Store<ESM4::SoundReference>, Store<ESM4::Static>, Store<ESM4::StaticCollection>,
Store<ESM4::Terminal>, Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>>;
Store<ESM4::Terminal>, Store<ESM4::TextureSet>, Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>>;
private:
template <typename T>

View file

@ -461,6 +461,11 @@ namespace MWWorld
update(magicBoltState, duration);
for (const auto& sound : magicBoltState.mSounds)
{
sound->setVelocity(direction * speed);
}
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit
// result.
std::vector<MWWorld::Ptr> targetActors;

View file

@ -1273,13 +1273,12 @@ namespace MWWorld
const std::size_t leftCapacity = mPreloader->getMaxCacheSize() - mPreloader->getCacheSize();
if (cells.size() > leftCapacity)
{
static bool logged = [&] {
[[maybe_unused]] static const bool logged = [&] {
Log(Debug::Warning) << "Not enough cell preloader cache capacity to preload exterior cells, consider "
"increasing \"preload cell cache max\" up to "
<< (mPreloader->getCacheSize() + cells.size());
return true;
}();
(void)logged;
cells.resize(leftCapacity);
}

View file

@ -1354,6 +1354,7 @@ template class MWWorld::TypedDynamicStore<ESM4::SoundReference>;
template class MWWorld::TypedDynamicStore<ESM4::Static>;
template class MWWorld::TypedDynamicStore<ESM4::StaticCollection>;
template class MWWorld::TypedDynamicStore<ESM4::Terminal>;
template class MWWorld::TypedDynamicStore<ESM4::TextureSet>;
template class MWWorld::TypedDynamicStore<ESM4::Tree>;
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
template class MWWorld::TypedDynamicStore<ESM4::World>;

View file

@ -1460,6 +1460,8 @@ namespace MWWorld
void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity)
{
mPhysics->queueObjectMovement(ptr, velocity);
if (ptr == MWMechanics::getPlayer())
MWBase::Environment::get().getSoundManager()->setListenerVel(velocity);
}
void World::updateAnimatedCollisionShape(const Ptr& ptr)
@ -3182,6 +3184,11 @@ namespace MWWorld
return mWeatherManager->getSunPercentage(getTimeStamp().getHour());
}
float World::getPhysicsFrameRateDt() const
{
return mPhysics->mPhysicsDt;
}
bool World::findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result)
{
if (cell->isExterior())

View file

@ -584,6 +584,8 @@ namespace MWWorld
float getSunVisibility() const override;
float getSunPercentage() const override;
float getPhysicsFrameRateDt() const override;
bool findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result) override;
/// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker)

View file

@ -407,6 +407,7 @@ add_component_dir(detournavigator
areatype
asyncnavmeshupdater
bounds
cellgridbounds
changetype
collisionshapetype
commulativeaabb

View file

@ -8,7 +8,6 @@
#include <algorithm>
#include <cstdint>
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>

View file

@ -10,10 +10,8 @@
#include <osg/io_utils>
#include <functional>
#include <memory>
#include <optional>
#include <stdexcept>
#include <vector>
namespace DetourNavigator
@ -79,7 +77,7 @@ namespace DetourNavigator
return;
}
const auto data
const std::unique_ptr<PreparedNavMeshData> data
= prepareNavMeshTileData(*recastMesh, mWorldspace, mTilePosition, mAgentBounds, mSettings.mRecast);
if (data == nullptr)

View file

@ -3,8 +3,6 @@
#include "exceptions.hpp"
#include "flags.hpp"
#include "navmeshdata.hpp"
#include "navmeshdb.hpp"
#include "navmeshtilescache.hpp"
#include "offmeshconnection.hpp"
#include "preparednavmeshdata.hpp"
#include "recastcontext.hpp"
@ -22,8 +20,6 @@
#include <algorithm>
#include <array>
#include <iomanip>
#include <limits>
namespace DetourNavigator
{

View file

@ -4,7 +4,6 @@
#include <DetourCommon.h>
#include <DetourNavMesh.h>
#include <algorithm>
#include <cassert>
#include <stdexcept>
#include <tuple>

View file

@ -6,7 +6,6 @@
#include <osg/Vec3f>
#include <tuple>
#include <vector>
namespace DetourNavigator
{

View file

@ -4,8 +4,6 @@
#include <Recast.h>
#include <cstring>
namespace
{
void initPolyMeshDetail(rcPolyMeshDetail& value) noexcept

View file

@ -3,9 +3,10 @@
#include "flags.hpp"
#include <optional>
#include <osg/Vec3f>
#include <optional>
class dtNavMeshQuery;
namespace DetourNavigator

View file

@ -11,9 +11,7 @@
#include <osg/Vec3f>
#include <cstdint>
#include <memory>
#include <numeric>
#include <string>
#include <tuple>
#include <vector>

View file

@ -31,6 +31,7 @@ namespace ESM
VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues
VER_094 = 0x3f70a3d7, // TES5/FO3
VER_170 = 0x3fd9999a, // TES5
VER_171 = 0x3fdae148, // TES5
VER_095 = 0x3f733333, // FO4
};

View file

@ -33,6 +33,7 @@ ESM::LandData::LandData(const ESM::Land& land, int loadFlags)
, mNormals(mData->mNormals)
, mColors(mData->mColours)
, mTextures(mData->mTextures)
, mIsEsm4(false)
{
}
@ -43,9 +44,11 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/)
, mMaxHeight(std::numeric_limits<float>::lowest())
, mSize(Constants::ESM4CellSizeInUnits)
, mLandSize(ESM4::Land::sVertsPerSide)
, mPlugin(land.mId.mContentFile)
, mNormals(land.mVertNorm)
, mColors(land.mVertColr)
, mTextures(textures)
, mIsEsm4(true)
{
float rowOffset = land.mHeightMap.heightOffset;
for (int y = 0; y < mLandSize; y++)
@ -69,6 +72,9 @@ ESM::LandData::LandData(const ESM4::Land& land, int /*loadFlags*/)
}
mHeights = mHeightsData;
for (int i = 0; i < 4; ++i)
mEsm4Textures[i] = land.mTextures[i];
}
namespace ESM

View file

@ -1,15 +1,13 @@
#ifndef COMPONENTS_ESM_ESMTERRAIN
#define COMPONENTS_ESM_ESMTERRAIN
#include <array>
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
namespace ESM4
{
struct Land;
}
#include <components/esm4/loadland.hpp>
namespace ESM
{
@ -28,7 +26,6 @@ namespace ESM
std::span<const float> getHeights() const { return mHeights; }
std::span<const std::int8_t> getNormals() const { return mNormals; }
std::span<const std::uint8_t> getColors() const { return mColors; }
std::span<const std::uint16_t> getTextures() const { return mTextures; }
float getSize() const { return mSize; }
float getMinHeight() const { return mMinHeight; }
float getMaxHeight() const { return mMaxHeight; }
@ -36,6 +33,22 @@ namespace ESM
int getLoadFlags() const { return mLoadFlags; }
int getPlugin() const { return mPlugin; }
bool isEsm4() const { return mIsEsm4; }
std::span<const std::uint16_t> getTextures() const
{
if (mIsEsm4)
throw std::logic_error("ESM3 textures requested from ESM4 LandData");
return mTextures;
}
const ESM4::Land::Texture& getEsm4Texture(std::size_t quad) const
{
if (!mIsEsm4)
throw std::logic_error("ESM4 texture requested from ESM3 LandData");
return mEsm4Textures[quad];
}
private:
std::unique_ptr<const ESM::LandRecordData> mData;
int mLoadFlags = 0;
@ -49,6 +62,8 @@ namespace ESM
std::span<const std::int8_t> mNormals;
std::span<const std::uint8_t> mColors;
std::span<const std::uint16_t> mTextures;
std::array<ESM4::Land::Texture, 4> mEsm4Textures;
bool mIsEsm4;
};
}

View file

@ -51,10 +51,10 @@ namespace std
{
size_t operator()(const ESM::FormId& formId) const
{
static_assert(sizeof(ESM::FormId) == sizeof(size_t));
size_t s;
memcpy(&s, &formId, sizeof(size_t));
return hash<size_t>()(s);
static_assert(sizeof(ESM::FormId) == sizeof(uint64_t));
uint64_t s;
memcpy(&s, &formId, sizeof(ESM::FormId));
return hash<uint64_t>()(s);
}
};

View file

@ -79,6 +79,7 @@
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadterm.hpp>
#include <components/esm4/loadtree.hpp>
#include <components/esm4/loadtxst.hpp>
#include <components/esm4/loadweap.hpp>
#include <components/esm4/loadwrld.hpp>

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2015-2016, 2018, 2020-2021 cc9cii
Copyright (C) 2015 - 2024 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@ -17,7 +17,7 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
cc9cii cc9cii@hotmail.com
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
@ -26,13 +26,51 @@
*/
#include "loadland.hpp"
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <components/debug/debuglog.hpp>
#include "reader.hpp"
// #include "writer.hpp"
namespace
{
void assignDefaultTextures(ESM4::Land& land, ESM4::Reader& reader)
{
std::uint32_t esmVer = reader.esmVersion();
// Note: in games after TES4 it can be configured in ini file (sDefaultLandDiffuseTexture)
if (!reader.hasFormVersion() && (esmVer == ESM::VER_080 || esmVer == ESM::VER_100)) // TES4
{
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01.dds");
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/terrainhddirt01_n.dds");
}
else if (reader.hasFormVersion() && reader.formVersion() >= 16
&& (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || esmVer == ESM::VER_171)) // TES5
{
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirt02.dds");
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirt02_n.dds");
}
else if (esmVer == ESM::VER_095 || esmVer == ESM::VER_100) // FO4
{
land.mDefaultDiffuseMap
= VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_d.dds");
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/ground/commonwealthdefault01_n.dds");
}
else if (esmVer == ESM::VER_094 || esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134)
{ // FO3, FONV
land.mDefaultDiffuseMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01.dds");
land.mDefaultNormalMap = VFS::Path::NormalizedView("textures/landscape/dirtwasteland01_n.dds");
}
else
{
// Nothing especially bad happens if default texture is not set (except of the missing texture of course),
// but we throw an error because this case is unexpected and detection logic needs to be updated.
throw std::runtime_error("ESM4::Land unknown ESM version");
}
}
}
// overlap north
//
@ -53,12 +91,16 @@ void ESM4::Land::load(ESM4::Reader& reader)
{
mId = reader.getFormIdFromHeader();
mFlags = reader.hdr().record.flags;
mDataTypes = 0;
mCell = reader.currCell();
TxtLayer layer;
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
assignDefaultTextures(*this, reader);
// std::map<FormId, int> uniqueTextures; // FIXME: for temp testing only
layer.texture.formId = 0;
for (int i = 0; i < 4; ++i)
mTextures[i].base.formId = 0;
while (reader.getSubRecordHeader())
{
@ -78,12 +120,6 @@ void ESM4::Land::load(ESM4::Reader& reader)
}
case ESM::fourCC("VHGT"): // vertex height gradient, 4+33x33+3 = 4+1089+3 = 1096
{
#if 0
reader.get(mHeightMap.heightOffset);
reader.get(mHeightMap.gradientData);
reader.get(mHeightMap.unknown1);
reader.get(mHeightMap.unknown2);
#endif
reader.get(mHeightMap);
mDataTypes |= LAND_VHGT;
break;
@ -102,13 +138,9 @@ void ESM4::Land::load(ESM4::Reader& reader)
if (base.quadrant >= 4)
throw std::runtime_error("base texture quadrant index error");
reader.adjustFormId(base.formId);
mTextures[base.quadrant].base = std::move(base);
#if 0
std::cout << "Base Texture formid: 0x"
<< std::hex << mTextures[base.quadrant].base.formId
<< ", quad " << std::dec << (int)base.quadrant << std::endl;
#endif
if (base.formId != 0)
reader.adjustFormId(base.formId);
mTextures[base.quadrant].base = base;
}
break;
}
@ -116,31 +148,23 @@ void ESM4::Land::load(ESM4::Reader& reader)
{
if (currentAddQuad != -1)
{
// FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex;
// NOTE: sometimes there are no VTXT following an ATXT
layer.data.resize(1); // just one spot
layer.data.back().position = 0; // this corner
layer.data.back().opacity = 0.f; // transparent
if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size())
throw std::runtime_error("ESM4::LAND additional texture skipping layer");
mTextures[currentAddQuad].layers.push_back(layer);
}
reader.get(layer.texture);
reader.adjustFormId(layer.texture.formId);
if (layer.texture.formId != 0)
reader.adjustFormId(layer.texture.formId);
if (layer.texture.quadrant >= 4)
throw std::runtime_error("additional texture quadrant index error");
#if 0
FormId txt = layer.texture.formId;
std::map<FormId, int>::iterator lb = uniqueTextures.lower_bound(txt);
if (lb != uniqueTextures.end() && !(uniqueTextures.key_comp()(txt, lb->first)))
{
lb->second += 1;
}
else
uniqueTextures.insert(lb, std::make_pair(txt, 1));
#endif
#if 0
std::cout << "Additional Texture formId: 0x"
<< std::hex << layer.texture.formId
<< ", quad " << std::dec << (int)layer.texture.quadrant << std::endl;
std::cout << "Additional Texture layer: "
<< std::dec << (int)layer.texture.layerIndex << std::endl;
#endif
throw std::runtime_error("ESM4::LAND additional texture quadrant index error");
currentAddQuad = layer.texture.quadrant;
break;
}
@ -156,25 +180,17 @@ void ESM4::Land::load(ESM4::Reader& reader)
if (count)
{
layer.data.resize(count);
std::vector<ESM4::Land::VTXT>::iterator it = layer.data.begin();
for (; it != layer.data.end(); ++it)
{
reader.get(*it);
// FIXME: debug only
// std::cout << "pos: " << std::dec << (int)(*it).position << std::endl;
}
for (ESM4::Land::VTXT& vtxt : layer.data)
reader.get(vtxt);
}
mTextures[currentAddQuad].layers.push_back(layer);
// Assumed that the layers are added in the correct sequence
// FIXME: Knights.esp doesn't seem to observe this - investigate more
// assert(layer.texture.layerIndex == mTextures[currentAddQuad].layers.size()-1
//&& "additional texture layer index error");
if (layer.texture.layerIndex != mTextures[currentAddQuad].layers.size())
throw std::runtime_error("ESM4::LAND additional texture skipping layer");
mTextures[currentAddQuad].layers.push_back(layer);
currentAddQuad = -1;
layer.data.clear();
// FIXME: debug only
// std::cout << "VTXT: count " << std::dec << count << std::endl;
break;
}
case ESM::fourCC("VTEX"): // only in Oblivion?
@ -195,44 +211,14 @@ void ESM4::Land::load(ESM4::Reader& reader)
reader.skipSubRecordData();
break;
default:
throw std::runtime_error("ESM4::LAND::load - Unknown subrecord " + ESM::printName(subHdr.typeId));
throw std::runtime_error("ESM4::LAND - Unknown subrecord " + ESM::printName(subHdr.typeId));
}
}
if (currentAddQuad != -1)
{
// FIXME: not sure if it happens here as well
// not sure if it happens here as well, if so just ignore
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << layer.texture.layerIndex << " quad "
<< static_cast<unsigned>(layer.texture.quadrant);
mTextures[currentAddQuad].layers.push_back(layer);
}
bool missing = false;
for (int i = 0; i < 4; ++i)
{
if (mTextures[i].base.formId == 0)
{
// std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl;
// std::cout << "layers " << mTextures[i].layers.size() << std::endl;
// NOTE: can't set the default here since FO3/FONV may have different defaults
// mTextures[i].base.formId = 0x000008C0; // TerrainHDDirt01.dds
missing = true;
}
// else
//{
// std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " base, quad " << i << std::endl;
// std::cout << "layers " << mTextures[i].layers.size() << std::endl;
// }
}
// at least one of the quadrants do not have a base texture, return without setting the flag
if (!missing)
mDataTypes |= LAND_VTEX;
}
// void ESM4::Land::save(ESM4::Writer& writer) const
//{
// }
// void ESM4::Land::blank()
//{
// }

View file

@ -32,6 +32,7 @@
#include <components/esm/defs.hpp>
#include <components/esm/formid.hpp>
#include <components/vfs/pathutil.hpp>
namespace ESM4
{
@ -124,6 +125,8 @@ namespace ESM4
Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right
std::vector<ESM::FormId> mIds; // land texture (LTEX) formids
ESM::FormId mCell;
VFS::Path::NormalizedView mDefaultDiffuseMap;
VFS::Path::NormalizedView mDefaultNormalMap;
void load(Reader& reader);

Some files were not shown because too many files have changed in this diff Show more