2012-07-03 12:30:50 +02:00
|
|
|
#include "worldimp.hpp"
|
2014-06-14 13:54:01 +02:00
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
#include <charconv>
|
2023-02-07 00:37:55 +01:00
|
|
|
#include <vector>
|
2022-08-27 13:07:59 +02:00
|
|
|
|
2015-05-22 01:43:45 +02:00
|
|
|
#include <osg/ComputeBoundsVisitor>
|
2015-04-12 15:34:50 +02:00
|
|
|
#include <osg/Group>
|
2020-11-20 13:11:53 +01:00
|
|
|
#include <osg/Timer>
|
2015-04-12 15:34:50 +02:00
|
|
|
|
2021-11-23 15:15:22 +01:00
|
|
|
#include <MyGUI_TextIterator.h>
|
|
|
|
|
2022-06-26 19:36:03 +02:00
|
|
|
#include <LinearMath/btAabbUtil2.h>
|
|
|
|
|
2018-08-14 23:05:43 +04:00
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/cellid.hpp>
|
|
|
|
#include <components/esm3/cellref.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/esmreader.hpp>
|
|
|
|
#include <components/esm3/esmwriter.hpp>
|
2022-09-06 14:19:55 +02:00
|
|
|
#include <components/esm3/loadclas.hpp>
|
2022-09-08 21:08:59 +02:00
|
|
|
#include <components/esm3/loadcrea.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadench.hpp>
|
|
|
|
#include <components/esm3/loadgmst.hpp>
|
2022-09-06 14:19:55 +02:00
|
|
|
#include <components/esm3/loadmgef.hpp>
|
2022-09-08 21:08:59 +02:00
|
|
|
#include <components/esm3/loadregn.hpp>
|
|
|
|
#include <components/esm3/loadstat.hpp>
|
2023-01-22 23:40:55 +01:00
|
|
|
#include <components/esm4/loadcell.hpp>
|
2023-01-22 19:03:19 +01:00
|
|
|
#include <components/esm4/loadstat.hpp>
|
2015-07-07 19:16:32 +02:00
|
|
|
|
2018-09-17 14:52:43 +04:00
|
|
|
#include <components/misc/constants.hpp>
|
2019-08-15 19:19:04 +02:00
|
|
|
#include <components/misc/convert.hpp>
|
2021-09-26 17:28:51 +02:00
|
|
|
#include <components/misc/mathutil.hpp>
|
2017-02-02 16:20:34 +09:00
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
2015-04-22 17:58:55 +02:00
|
|
|
#include <components/misc/rng.hpp>
|
2015-03-15 14:07:47 +13:00
|
|
|
|
2011-05-05 19:32:42 +02:00
|
|
|
#include <components/files/collections.hpp>
|
2017-02-02 16:20:34 +09:00
|
|
|
|
2018-07-12 11:44:11 +03:00
|
|
|
#include <components/resource/bulletshape.hpp>
|
2015-05-12 17:40:42 +02:00
|
|
|
#include <components/resource/resourcesystem.hpp>
|
2010-07-02 09:38:22 +02:00
|
|
|
|
2022-05-14 22:53:53 -07:00
|
|
|
#include <components/sceneutil/lightmanager.hpp>
|
2015-11-20 21:57:04 +01:00
|
|
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
2022-07-24 20:55:03 +02:00
|
|
|
#include <components/sceneutil/workqueue.hpp>
|
2015-11-20 21:57:04 +01:00
|
|
|
|
2022-08-12 00:09:49 +02:00
|
|
|
#include <components/detournavigator/agentbounds.hpp>
|
2023-01-17 23:31:17 +01:00
|
|
|
#include <components/detournavigator/debug.hpp>
|
2021-11-06 00:14:41 +01:00
|
|
|
#include <components/detournavigator/navigator.hpp>
|
2022-09-05 09:23:14 +02:00
|
|
|
#include <components/detournavigator/navigatorimpl.hpp>
|
2021-11-06 00:34:06 +01:00
|
|
|
#include <components/detournavigator/settings.hpp>
|
2022-08-28 14:52:12 +02:00
|
|
|
#include <components/detournavigator/stats.hpp>
|
2018-03-14 01:49:08 +03:00
|
|
|
|
2022-07-03 00:02:29 +02:00
|
|
|
#include <components/files/conversion.hpp>
|
2021-11-23 15:15:22 +01:00
|
|
|
#include <components/loadinglistener/loadinglistener.hpp>
|
|
|
|
|
2012-04-23 15:27:03 +02:00
|
|
|
#include "../mwbase/environment.hpp"
|
2012-08-09 14:33:21 +02:00
|
|
|
#include "../mwbase/luamanager.hpp"
|
2012-08-11 17:30:55 +02:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2015-05-12 03:02:15 +02:00
|
|
|
#include "../mwbase/scriptmanager.hpp"
|
2020-12-19 00:02:31 +01:00
|
|
|
#include "../mwbase/soundmanager.hpp"
|
|
|
|
#include "../mwbase/windowmanager.hpp"
|
2012-04-23 15:27:03 +02:00
|
|
|
|
2022-06-26 16:42:29 +02:00
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
2014-05-13 03:58:32 -04:00
|
|
|
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
|
2014-03-08 05:51:47 +01:00
|
|
|
#include "../mwmechanics/combat.hpp"
|
2013-02-20 01:55:12 -08:00
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
2014-01-14 07:40:17 +01:00
|
|
|
#include "../mwmechanics/levelledlist.hpp"
|
2013-08-05 23:23:39 +02:00
|
|
|
#include "../mwmechanics/npcstats.hpp"
|
2013-11-16 02:34:43 +01:00
|
|
|
#include "../mwmechanics/spellcasting.hpp"
|
2021-04-02 19:47:21 +02:00
|
|
|
#include "../mwmechanics/spellutil.hpp"
|
2020-04-04 16:27:00 +03:00
|
|
|
#include "../mwmechanics/summoning.hpp"
|
2013-02-20 01:55:12 -08:00
|
|
|
|
2015-05-22 04:36:17 +02:00
|
|
|
#include "../mwrender/animation.hpp"
|
2015-05-21 23:54:39 +02:00
|
|
|
#include "../mwrender/camera.hpp"
|
2017-02-22 14:54:40 +01:00
|
|
|
#include "../mwrender/npcanimation.hpp"
|
2022-05-13 18:58:00 -07:00
|
|
|
#include "../mwrender/postprocessor.hpp"
|
2015-04-12 15:34:50 +02:00
|
|
|
#include "../mwrender/renderingmanager.hpp"
|
2020-04-20 18:47:14 +02:00
|
|
|
#include "../mwrender/vismask.hpp"
|
2010-07-27 14:05:05 +02:00
|
|
|
|
2015-05-12 03:02:15 +02:00
|
|
|
#include "../mwscript/globalscripts.hpp"
|
2014-06-13 00:01:29 +02:00
|
|
|
|
2012-12-23 23:23:24 +04:00
|
|
|
#include "../mwclass/door.hpp"
|
|
|
|
|
2015-05-12 19:02:56 +02:00
|
|
|
#include "../mwphysics/actor.hpp"
|
2015-05-24 03:59:22 +02:00
|
|
|
#include "../mwphysics/collisiontype.hpp"
|
2018-05-26 17:44:25 +03:00
|
|
|
#include "../mwphysics/object.hpp"
|
2015-05-12 03:02:15 +02:00
|
|
|
#include "../mwphysics/physicssystem.hpp"
|
|
|
|
|
2020-06-03 11:32:28 +04:00
|
|
|
#include "actionteleport.hpp"
|
2015-02-09 15:12:10 +01:00
|
|
|
#include "cellstore.hpp"
|
2013-01-27 03:03:48 -08:00
|
|
|
#include "containerstore.hpp"
|
2015-06-01 21:41:13 +02:00
|
|
|
#include "datetimemanager.hpp"
|
2013-04-15 02:56:23 +02:00
|
|
|
#include "inventorystore.hpp"
|
2014-01-01 02:22:11 +01:00
|
|
|
#include "manualref.hpp"
|
2011-01-04 15:58:22 +01:00
|
|
|
#include "player.hpp"
|
2015-06-01 21:41:13 +02:00
|
|
|
#include "projectilemanager.hpp"
|
2015-04-01 17:02:15 +02:00
|
|
|
#include "weather.hpp"
|
2010-11-06 13:11:09 -04:00
|
|
|
|
2022-05-06 20:36:25 +02:00
|
|
|
#include "cellutils.hpp"
|
2013-09-29 09:11:57 +02:00
|
|
|
#include "contentloader.hpp"
|
|
|
|
#include "esmloader.hpp"
|
|
|
|
|
2010-07-03 15:41:20 +02:00
|
|
|
namespace MWWorld
|
2010-07-02 09:38:22 +02:00
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
std::vector<std::pair<std::string_view, ESM::Variant>> generateDefaultGameSettings()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
// Companion (tribunal)
|
|
|
|
{ "sCompanionShare", ESM::Variant("Companion Share") },
|
|
|
|
{ "sCompanionWarningMessage", ESM::Variant("Warning message") },
|
|
|
|
{ "sCompanionWarningButtonOne", ESM::Variant("Button 1") },
|
|
|
|
{ "sCompanionWarningButtonTwo", ESM::Variant("Button 2") },
|
|
|
|
{ "sProfitValue", ESM::Variant("Profit Value") },
|
|
|
|
{ "sTeleportDisabled", ESM::Variant("Teleport disabled") },
|
|
|
|
{ "sLevitateDisabled", ESM::Variant("Levitate disabled") },
|
|
|
|
// Missing in unpatched MW 1.0
|
|
|
|
{ "sDifficulty", ESM::Variant("Difficulty") },
|
|
|
|
{ "fDifficultyMult", ESM::Variant(5.f) },
|
|
|
|
{ "sAuto_Run", ESM::Variant("Auto Run") },
|
|
|
|
{ "sServiceRefusal", ESM::Variant("Service Refusal") },
|
|
|
|
{ "sNeedOneSkill", ESM::Variant("Need one skill") },
|
|
|
|
{ "sNeedTwoSkills", ESM::Variant("Need two skills") },
|
|
|
|
{ "sEasy", ESM::Variant("Easy") },
|
|
|
|
{ "sHard", ESM::Variant("Hard") },
|
|
|
|
{ "sDeleteNote", ESM::Variant("Delete Note") },
|
|
|
|
{ "sEditNote", ESM::Variant("Edit Note") },
|
|
|
|
{ "sAdmireSuccess", ESM::Variant("Admire Success") },
|
|
|
|
{ "sAdmireFail", ESM::Variant("Admire Fail") },
|
|
|
|
{ "sIntimidateSuccess", ESM::Variant("Intimidate Success") },
|
|
|
|
{ "sIntimidateFail", ESM::Variant("Intimidate Fail") },
|
|
|
|
{ "sTauntSuccess", ESM::Variant("Taunt Success") },
|
|
|
|
{ "sTauntFail", ESM::Variant("Taunt Fail") },
|
|
|
|
{ "sBribeSuccess", ESM::Variant("Bribe Success") },
|
|
|
|
{ "sBribeFail", ESM::Variant("Bribe Fail") },
|
|
|
|
{ "fNPCHealthBarTime", ESM::Variant(5.f) },
|
|
|
|
{ "fNPCHealthBarFade", ESM::Variant(1.f) },
|
|
|
|
{ "fFleeDistance", ESM::Variant(3000.f) },
|
|
|
|
{ "sMaxSale", ESM::Variant("Max Sale") },
|
|
|
|
{ "sAnd", ESM::Variant("and") },
|
|
|
|
// Werewolf (BM)
|
|
|
|
{ "fWereWolfRunMult", ESM::Variant(1.3f) },
|
|
|
|
{ "fWereWolfSilverWeaponDamageMult", ESM::Variant(2.f) },
|
|
|
|
{ "iWerewolfFightMod", ESM::Variant(100) },
|
|
|
|
{ "iWereWolfFleeMod", ESM::Variant(100) },
|
|
|
|
{ "iWereWolfLevelToAttack", ESM::Variant(20) },
|
|
|
|
{ "iWereWolfBounty", ESM::Variant(1000) },
|
|
|
|
{ "fCombatDistanceWerewolfMod", ESM::Variant(0.3f) },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<GlobalVariableName, ESM::Variant>> generateDefaultGlobals()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
// vanilla Morrowind does not define dayspassed.
|
|
|
|
{ Globals::sDaysPassed, ESM::Variant(1) }, // but the addons start counting at 1 :(
|
|
|
|
{ Globals::sWerewolfClawMult, ESM::Variant(25.f) },
|
|
|
|
{ Globals::sPCKnownWerewolf, ESM::Variant(0) },
|
|
|
|
// following should exist in all versions of MW, but not necessarily in TCs
|
|
|
|
{ Globals::sGameHour, ESM::Variant(0) },
|
|
|
|
{ Globals::sTimeScale, ESM::Variant(30.f) },
|
|
|
|
{ Globals::sDay, ESM::Variant(1) },
|
|
|
|
{ Globals::sYear, ESM::Variant(1) },
|
|
|
|
{ Globals::sPCRace, ESM::Variant(0) },
|
|
|
|
{ Globals::sPCHasCrimeGold, ESM::Variant(0) },
|
|
|
|
{ Globals::sCrimeGoldDiscount, ESM::Variant(0) },
|
|
|
|
{ Globals::sCrimeGoldTurnIn, ESM::Variant(0) },
|
|
|
|
{ Globals::sPCHasTurnIn, ESM::Variant(0) },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string_view, std::string_view>> generateDefaultStatics()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
// Total conversions from SureAI lack marker records
|
|
|
|
{ "divinemarker", "marker_divine.nif" },
|
|
|
|
{ "doormarker", "marker_arrow.nif" },
|
|
|
|
{ "northmarker", "marker_north.nif" },
|
|
|
|
{ "templemarker", "marker_temple.nif" },
|
|
|
|
{ "travelmarker", "marker_travel.nif" },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string_view, std::string_view>> generateDefaultDoors()
|
|
|
|
{
|
|
|
|
return { { "prisonmarker", "marker_prison.nif" } };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-29 09:11:57 +02:00
|
|
|
struct GameContentLoader : public ContentLoader
|
|
|
|
{
|
2021-11-23 15:15:22 +01:00
|
|
|
void addLoader(std::string&& extension, ContentLoader& loader)
|
2013-09-29 09:11:57 +02:00
|
|
|
{
|
2021-11-23 15:15:22 +01:00
|
|
|
mLoaders.emplace(std::move(extension), &loader);
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
|
|
|
|
2022-06-08 23:25:50 +02:00
|
|
|
void load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener) override
|
2013-09-29 09:11:57 +02:00
|
|
|
{
|
2022-07-03 00:02:29 +02:00
|
|
|
const auto it
|
|
|
|
= mLoaders.find(Misc::StringUtils::lowerCase(Files::pathToUnicodeString(filepath.extension())));
|
2013-09-29 09:11:57 +02:00
|
|
|
if (it != mLoaders.end())
|
|
|
|
{
|
2022-07-03 00:02:29 +02:00
|
|
|
const auto filename = filepath.filename();
|
2021-11-23 15:15:22 +01:00
|
|
|
Log(Debug::Info) << "Loading content file " << filename;
|
|
|
|
if (listener != nullptr)
|
2022-07-03 00:02:29 +02:00
|
|
|
listener->setLabel(MyGUI::TextIterator::toTagsString(Files::pathToUnicodeString(filename)));
|
2021-11-23 15:15:22 +01:00
|
|
|
it->second->load(filepath, index, listener);
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-23 15:15:22 +01:00
|
|
|
std::string msg("Cannot load file: ");
|
2022-07-03 00:02:29 +02:00
|
|
|
msg += Files::pathToUnicodeString(filepath);
|
2021-11-23 15:15:22 +01:00
|
|
|
throw std::runtime_error(msg.c_str());
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-11-23 15:15:22 +01:00
|
|
|
std::map<std::string, ContentLoader*> mLoaders;
|
2013-09-29 09:11:57 +02:00
|
|
|
};
|
|
|
|
|
2021-09-26 15:21:46 +02:00
|
|
|
struct OMWScriptsLoader : public ContentLoader
|
|
|
|
{
|
|
|
|
ESMStore& mStore;
|
2021-11-23 15:15:22 +01:00
|
|
|
OMWScriptsLoader(ESMStore& store)
|
|
|
|
: mStore(store)
|
|
|
|
{
|
|
|
|
}
|
2022-06-08 23:25:50 +02:00
|
|
|
void load(const std::filesystem::path& filepath, int& /*index*/, Loading::Listener* /*listener*/) override
|
2021-09-26 15:21:46 +02:00
|
|
|
{
|
2022-06-19 13:28:33 +02:00
|
|
|
mStore.addOMWScripts(filepath);
|
2021-09-26 15:21:46 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-08-22 20:55:22 +02:00
|
|
|
void World::adjustSky()
|
|
|
|
{
|
2012-02-26 18:21:11 +01:00
|
|
|
if (mSky && (isCellExterior() || isCellQuasiExterior()))
|
2010-08-22 20:55:22 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
updateSkyDate();
|
2015-04-14 15:55:56 +02:00
|
|
|
mRendering->setSkyEnabled(true);
|
2010-08-22 20:55:22 +02:00
|
|
|
}
|
2012-02-26 18:21:11 +01:00
|
|
|
else
|
2015-04-14 15:55:56 +02:00
|
|
|
mRendering->setSkyEnabled(false);
|
2010-08-22 20:55:22 +02:00
|
|
|
}
|
|
|
|
|
2017-02-14 03:37:45 +01:00
|
|
|
World::World(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem,
|
2011-05-05 19:32:42 +02:00
|
|
|
SceneUtil::WorkQueue* workQueue, SceneUtil::UnrefQueue& unrefQueue, const Files::Collections& fileCollections,
|
2020-01-12 11:42:47 +04:00
|
|
|
const std::vector<std::string>& contentFiles, const std::vector<std::string>& groundcoverFiles,
|
2023-01-19 17:31:45 +01:00
|
|
|
ToUTF8::Utf8Encoder* encoder, int activationDistanceOverride, const std::string& startCell,
|
2023-01-29 11:14:08 -08:00
|
|
|
const std::string& startupScript, const std::filesystem::path& userDataPath)
|
2022-06-01 22:53:18 +02:00
|
|
|
: mResourceSystem(resourceSystem)
|
|
|
|
, mLocalScripts(mStore)
|
2022-12-15 20:56:17 +01:00
|
|
|
, mWorldModel(mStore, mReaders)
|
2022-06-01 22:53:18 +02:00
|
|
|
, mSky(true)
|
Process movement queue in one or several background threads
Before movement calculation, the main thread prepare a
vector of ActorFrameData, which contains all data necessary to perform
the simulation, and feed it to the solver. At the same time it fetches
the result from the previous background simulation, which in turn is
used by the game mechanics.
Other functions of the physics system (weapon hit for instance)
interrupt the background simulation, with some exceptions described
below.
The number of threads is controlled by the numeric setting
[Physics]
async num threads
In case 'async num threads' > 1 and Bullet doesn't support multiple threads,
1 async thread will be used. 0 means synchronous solver.
Additional settings (will be silently switched off if async num threads = 0)
[Physics]
defer aabb update
Update AABBs of actors and objects in the background thread(s). It is not an especially
costly operation, but it needs exclusive access to the collision world, which blocks
other operations. Since AABB needs to be updated for collision detection, one can queue
them to defer update before start of the movement solver. Extensive tests on as much
as one installation (mine) show no drawback having that switched on.
[Physics]
lineofsight keep inactive cache
Control for how long (how many frames) the line of sight (LOS) request will be kept updated.
When a request for LOS is made for the first time, the background threads are stopped to
service it. From now on, the LOS will be refreshed preemptively as part of the background
routine until it is not required for lineofsight keep inactive cache frames. This mean
that subsequent request will not interrupt the background computation.
2020-10-15 06:11:44 +02:00
|
|
|
, mGodMode(false)
|
|
|
|
, mScriptsEnabled(true)
|
|
|
|
, mDiscardMovements(true)
|
|
|
|
, mContentFiles(contentFiles)
|
2021-06-29 21:41:15 +02:00
|
|
|
, mUserDataPath(userDataPath)
|
|
|
|
, mDefaultHalfExtents(Settings::Manager::getVector3("default actor pathfind half extents", "Game"))
|
2022-06-04 00:44:42 +02:00
|
|
|
, mDefaultActorCollisionShapeType(
|
|
|
|
DetourNavigator::toCollisionShapeType(Settings::Manager::getInt("actor collision shape type", "Game")))
|
2019-02-23 22:39:13 +03:00
|
|
|
, mActivationDistanceOverride(activationDistanceOverride)
|
2020-05-29 13:37:13 +04:00
|
|
|
, mStartCell(startCell)
|
|
|
|
, mDistanceToFacedObject(-1.f)
|
|
|
|
, mTeleportEnabled(true)
|
2018-08-13 08:30:50 +04:00
|
|
|
, mLevitationEnabled(true)
|
|
|
|
, mGoToJail(false)
|
|
|
|
, mDaysInPrison(0)
|
2018-08-13 11:10:01 +04:00
|
|
|
, mPlayerTraveling(false)
|
|
|
|
, mPlayerInJail(false)
|
|
|
|
, mSpellPreloadTimer(0.f)
|
2010-08-03 20:01:52 +02:00
|
|
|
{
|
2023-03-06 20:28:55 +01:00
|
|
|
if (encoder)
|
|
|
|
mReaders.setStatelessEncoder(encoder->getStatelessEncoder());
|
2022-09-03 18:50:38 +02:00
|
|
|
mESMVersions.resize(mContentFiles.size(), -1);
|
2018-07-20 22:11:34 +03:00
|
|
|
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
|
|
|
listener->loadingOn();
|
2021-10-29 16:47:17 +04:00
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
loadContentFiles(fileCollections, contentFiles, encoder, listener);
|
2022-06-03 18:59:08 +02:00
|
|
|
loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder, listener);
|
2018-07-20 22:11:34 +03:00
|
|
|
|
|
|
|
listener->loadingOff();
|
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
mCurrentDate = std::make_unique<DateTimeManager>();
|
2020-06-03 11:32:28 +04:00
|
|
|
|
2018-07-20 22:11:34 +03:00
|
|
|
fillGlobalVariables();
|
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
mStore.setUp();
|
|
|
|
mStore.validateRecords(mReaders);
|
2018-07-20 22:11:34 +03:00
|
|
|
mStore.movePlayerRecord();
|
|
|
|
|
|
|
|
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
mPhysics = std::make_unique<MWPhysics::PhysicsSystem>(resourceSystem, rootNode);
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2021-07-03 04:09:55 +02:00
|
|
|
if (Settings::Manager::getBool("enable", "Navigator"))
|
2019-02-16 15:50:58 +03:00
|
|
|
{
|
2021-07-03 04:09:55 +02:00
|
|
|
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
|
2021-11-06 13:46:43 +01:00
|
|
|
navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale;
|
2021-07-09 22:51:42 +02:00
|
|
|
mNavigator = DetourNavigator::makeNavigator(navigatorSettings, userDataPath);
|
2019-02-16 15:50:58 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-11-06 00:14:41 +01:00
|
|
|
mNavigator = DetourNavigator::makeNavigatorStub();
|
2019-02-16 15:50:58 +03:00
|
|
|
}
|
2018-03-14 01:49:08 +03:00
|
|
|
|
2022-07-24 20:55:03 +02:00
|
|
|
mRendering = std::make_unique<MWRender::RenderingManager>(
|
2023-01-29 11:14:08 -08:00
|
|
|
viewer, rootNode, resourceSystem, workQueue, *mNavigator, mGroundcoverStore, unrefQueue);
|
2022-05-29 13:24:48 +02:00
|
|
|
mProjectileManager = std::make_unique<ProjectileManager>(
|
|
|
|
mRendering->getLightRoot()->asGroup(), resourceSystem, mRendering.get(), mPhysics.get());
|
2017-02-09 02:59:10 +01:00
|
|
|
mRendering->preloadCommonAssets();
|
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
mWeatherManager = std::make_unique<MWWorld::WeatherManager>(*mRendering, mStore);
|
2015-07-10 02:34:00 +02:00
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
mWorldScene = std::make_unique<Scene>(*this, *mRendering.get(), mPhysics.get(), *mNavigator);
|
2013-05-15 17:54:18 +02:00
|
|
|
}
|
|
|
|
|
2015-11-22 22:13:21 +01:00
|
|
|
void World::fillGlobalVariables()
|
|
|
|
{
|
|
|
|
mGlobalVariables.fill(mStore);
|
2020-06-03 11:32:28 +04:00
|
|
|
mCurrentDate->setup(mGlobalVariables);
|
2015-11-22 22:13:21 +01:00
|
|
|
}
|
|
|
|
|
2014-03-13 13:19:32 +01:00
|
|
|
void World::startNewGame(bool bypass)
|
2013-05-15 17:54:18 +02:00
|
|
|
{
|
2014-01-11 06:47:58 +01:00
|
|
|
mGoToJail = false;
|
|
|
|
mLevitationEnabled = true;
|
|
|
|
mTeleportEnabled = true;
|
2013-05-15 17:54:18 +02:00
|
|
|
|
2015-09-26 01:49:58 +02:00
|
|
|
mGodMode = false;
|
|
|
|
mScriptsEnabled = true;
|
|
|
|
mSky = true;
|
|
|
|
|
2013-05-15 17:54:18 +02:00
|
|
|
// Rebuild player
|
|
|
|
setupPlayer();
|
|
|
|
|
2013-08-20 11:04:26 +02:00
|
|
|
renderPlayer();
|
2020-07-25 23:33:50 +02:00
|
|
|
mRendering->getCamera()->reset();
|
2013-05-15 17:54:18 +02:00
|
|
|
|
2015-08-26 22:59:21 -05:00
|
|
|
// we don't want old weather to persist on a new game
|
|
|
|
// Note that if reset later, the initial ChangeWeather that the chargen script calls will be lost.
|
2018-01-30 22:05:16 +00:00
|
|
|
mWeatherManager.reset();
|
2022-05-29 13:24:48 +02:00
|
|
|
mWeatherManager = std::make_unique<MWWorld::WeatherManager>(*mRendering.get(), mStore);
|
2015-08-26 22:59:21 -05:00
|
|
|
|
2014-05-19 14:09:16 +02:00
|
|
|
if (!bypass)
|
|
|
|
{
|
|
|
|
// set new game mark
|
2023-02-07 00:37:55 +01:00
|
|
|
mGlobalVariables[Globals::sCharGenState].setInteger(1);
|
2014-05-19 14:09:16 +02:00
|
|
|
}
|
2014-07-25 12:23:18 +02:00
|
|
|
else
|
2023-02-07 00:37:55 +01:00
|
|
|
mGlobalVariables[Globals::sCharGenState].setInteger(-1);
|
2014-05-19 14:09:16 +02:00
|
|
|
|
2014-03-13 13:19:32 +01:00
|
|
|
if (bypass && !mStartCell.empty())
|
|
|
|
{
|
|
|
|
ESM::Position pos;
|
|
|
|
if (findExteriorPosition(mStartCell, pos))
|
|
|
|
{
|
2016-03-24 17:18:08 +01:00
|
|
|
changeToExteriorCell(pos, true);
|
2018-09-13 13:21:38 +04:00
|
|
|
adjustPosition(getPlayerPtr(), false);
|
2014-03-13 13:19:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
findInteriorPosition(mStartCell, pos);
|
2016-03-24 17:18:08 +01:00
|
|
|
changeToInteriorCell(mStartCell, pos, true);
|
2014-03-13 13:19:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-05-19 14:09:16 +02:00
|
|
|
for (int i = 0; i < 5; ++i)
|
|
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
|
|
|
|
if (!getPlayerPtr().isInCell())
|
|
|
|
{
|
|
|
|
ESM::Position pos;
|
2018-09-17 14:52:43 +04:00
|
|
|
const int cellSize = Constants::CellSizeInUnits;
|
2014-05-19 14:09:16 +02:00
|
|
|
pos.pos[0] = cellSize / 2;
|
|
|
|
pos.pos[1] = cellSize / 2;
|
|
|
|
pos.pos[2] = 0;
|
|
|
|
pos.rot[0] = 0;
|
|
|
|
pos.rot[1] = 0;
|
|
|
|
pos.rot[2] = 0;
|
2014-10-02 16:30:23 +02:00
|
|
|
mWorldScene->changeToExteriorCell(pos, true);
|
2014-05-19 14:09:16 +02:00
|
|
|
}
|
2014-03-13 13:19:32 +01:00
|
|
|
}
|
2012-12-04 10:58:43 +01:00
|
|
|
|
2014-05-20 09:45:39 +02:00
|
|
|
if (!bypass)
|
2015-05-23 05:42:21 +02:00
|
|
|
{
|
2022-08-28 17:20:49 +02:00
|
|
|
std::string_view video = Fallback::Map::getString("Movies_New_Game");
|
2015-05-23 05:42:21 +02:00
|
|
|
if (!video.empty())
|
|
|
|
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
|
|
|
|
}
|
2014-05-20 09:45:39 +02:00
|
|
|
|
2014-05-20 17:11:26 +02:00
|
|
|
// enable collision
|
2015-05-23 06:07:11 +02:00
|
|
|
if (!mPhysics->toggleCollisionMode())
|
|
|
|
mPhysics->toggleCollisionMode();
|
2014-05-20 17:11:26 +02:00
|
|
|
|
2017-04-21 00:51:13 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
2020-06-03 11:32:28 +04:00
|
|
|
mCurrentDate->setup(mGlobalVariables);
|
2022-03-17 14:30:00 +02:00
|
|
|
|
|
|
|
// Initial seed.
|
|
|
|
mPrng.seed(mRandomSeed);
|
2010-07-02 09:38:22 +02:00
|
|
|
}
|
2012-04-04 16:08:46 +02:00
|
|
|
|
2013-12-05 12:49:25 +01:00
|
|
|
void World::clear()
|
|
|
|
{
|
2015-04-14 15:55:56 +02:00
|
|
|
mWeatherManager->clear();
|
2015-04-19 17:55:56 +02:00
|
|
|
mRendering->clear();
|
2014-05-17 05:21:17 +02:00
|
|
|
mProjectileManager->clear();
|
2013-12-05 12:49:25 +01:00
|
|
|
mLocalScripts.clear();
|
|
|
|
|
2017-02-09 04:03:38 +01:00
|
|
|
mWorldScene->clear();
|
2022-12-23 19:57:12 +01:00
|
|
|
mWorldModel.clear();
|
2013-12-05 12:49:25 +01:00
|
|
|
|
2014-01-18 14:53:25 +01:00
|
|
|
mStore.clearDynamic();
|
|
|
|
|
2013-12-05 12:49:25 +01:00
|
|
|
if (mPlayer)
|
2013-12-07 13:17:28 +01:00
|
|
|
{
|
2015-09-16 20:45:37 +02:00
|
|
|
mPlayer->clear();
|
2020-11-13 11:39:47 +04:00
|
|
|
mPlayer->setCell(nullptr);
|
2013-12-05 12:49:25 +01:00
|
|
|
mPlayer->getPlayer().getRefData() = RefData();
|
2022-10-18 09:26:55 +02:00
|
|
|
mPlayer->set(mStore.get<ESM::NPC>().find(ESM::RefId::stringRefId("Player")));
|
2013-12-07 13:17:28 +01:00
|
|
|
}
|
2013-12-05 12:49:25 +01:00
|
|
|
|
2013-12-05 14:18:43 +01:00
|
|
|
mDoorStates.clear();
|
|
|
|
|
2017-06-01 16:33:52 -05:00
|
|
|
mGoToJail = false;
|
2013-12-05 14:18:43 +01:00
|
|
|
mTeleportEnabled = true;
|
2014-09-21 19:51:33 +02:00
|
|
|
mLevitationEnabled = true;
|
2018-08-13 08:30:50 +04:00
|
|
|
mPlayerTraveling = false;
|
2018-08-13 11:10:01 +04:00
|
|
|
mPlayerInJail = false;
|
2013-12-10 15:09:58 +01:00
|
|
|
|
2015-11-22 22:13:21 +01:00
|
|
|
fillGlobalVariables();
|
2010-07-02 09:38:22 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2013-12-07 13:17:28 +01:00
|
|
|
int World::countSavedGameRecords() const
|
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
return mWorldModel.countSavedGameRecords() + mStore.countSavedGameRecords()
|
2014-05-17 05:21:17 +02:00
|
|
|
+ mGlobalVariables.countSavedGameRecords() + mProjectileManager->countSavedGameRecords()
|
2014-01-23 11:29:40 +01:00
|
|
|
+ 1 // player record
|
2014-05-14 09:47:49 +02:00
|
|
|
+ 1 // weather record
|
2014-09-21 13:37:20 +02:00
|
|
|
+ 1 // actorId counter
|
2015-01-06 19:29:33 +01:00
|
|
|
+ 1 // levitation/teleport enabled state
|
2022-04-21 11:51:10 +03:00
|
|
|
+ 1 // camera
|
|
|
|
+ 1; // random state.
|
2013-12-07 13:17:28 +01:00
|
|
|
}
|
|
|
|
|
2015-01-11 18:01:06 +01:00
|
|
|
int World::countSavedGameCells() const
|
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
return mWorldModel.countSavedGameRecords();
|
2015-01-11 18:01:06 +01:00
|
|
|
}
|
|
|
|
|
2014-04-28 11:29:57 +02:00
|
|
|
void World::write(ESM::ESMWriter& writer, Loading::Listener& progress) const
|
2013-12-07 13:17:28 +01:00
|
|
|
{
|
2022-03-07 12:03:13 +02:00
|
|
|
writer.startRecord(ESM::REC_RAND);
|
2022-03-17 17:35:34 +02:00
|
|
|
writer.writeHNOString("RAND", Misc::Rng::serialize(mPrng));
|
2022-03-07 12:03:13 +02:00
|
|
|
writer.endRecord(ESM::REC_RAND);
|
|
|
|
|
2014-05-11 02:07:28 +02:00
|
|
|
// Active cells could have a dirty fog of war, sync it to the CellStore first
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2014-05-11 02:07:28 +02:00
|
|
|
{
|
2015-05-28 03:50:44 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->writeFog(cellstore);
|
2014-05-11 02:07:28 +02:00
|
|
|
}
|
|
|
|
|
2014-05-14 09:47:49 +02:00
|
|
|
MWMechanics::CreatureStats::writeActorIdCounter(writer);
|
|
|
|
|
2014-07-28 00:32:59 +02:00
|
|
|
mStore.write(writer, progress); // dynamic Store must be written (and read) before Cells, so that
|
|
|
|
// references to custom made records will be recognized
|
2017-02-09 03:48:33 +01:00
|
|
|
mPlayer->write(writer, progress);
|
2022-12-15 20:56:17 +01:00
|
|
|
mWorldModel.write(writer, progress);
|
2014-04-28 11:29:57 +02:00
|
|
|
mGlobalVariables.write(writer, progress);
|
2015-04-14 15:55:56 +02:00
|
|
|
mWeatherManager->write(writer, progress);
|
2014-05-17 05:21:17 +02:00
|
|
|
mProjectileManager->write(writer, progress);
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2014-09-21 13:37:20 +02:00
|
|
|
writer.startRecord(ESM::REC_ENAB);
|
|
|
|
writer.writeHNT("TELE", mTeleportEnabled);
|
|
|
|
writer.writeHNT("LEVT", mLevitationEnabled);
|
|
|
|
writer.endRecord(ESM::REC_ENAB);
|
2015-01-06 19:29:33 +01:00
|
|
|
|
|
|
|
writer.startRecord(ESM::REC_CAM_);
|
|
|
|
writer.writeHNT("FIRS", isFirstPerson());
|
|
|
|
writer.endRecord(ESM::REC_CAM_);
|
2013-12-07 13:17:28 +01:00
|
|
|
}
|
|
|
|
|
2015-01-22 19:04:59 +01:00
|
|
|
void World::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
2013-12-07 13:17:28 +01:00
|
|
|
{
|
2014-09-21 13:37:20 +02:00
|
|
|
switch (type)
|
2013-12-07 13:17:28 +01:00
|
|
|
{
|
2014-09-21 13:37:20 +02:00
|
|
|
case ESM::REC_ACTC:
|
|
|
|
MWMechanics::CreatureStats::readActorIdCounter(reader);
|
|
|
|
return;
|
|
|
|
case ESM::REC_ENAB:
|
|
|
|
reader.getHNT(mTeleportEnabled, "TELE");
|
|
|
|
reader.getHNT(mLevitationEnabled, "LEVT");
|
|
|
|
return;
|
2022-03-06 20:02:03 +02:00
|
|
|
case ESM::REC_RAND:
|
|
|
|
{
|
2022-03-17 17:35:34 +02:00
|
|
|
auto data = reader.getHNOString("RAND");
|
|
|
|
Misc::Rng::deserialize(data, mPrng);
|
2022-03-06 20:02:03 +02:00
|
|
|
}
|
|
|
|
break;
|
2017-02-09 03:47:36 +01:00
|
|
|
case ESM::REC_PLAY:
|
2020-06-02 21:59:37 +02:00
|
|
|
mStore.checkPlayer();
|
2017-02-09 03:47:36 +01:00
|
|
|
mPlayer->readRecord(reader, type);
|
2017-06-30 00:51:26 +02:00
|
|
|
if (getPlayerPtr().isInCell())
|
|
|
|
{
|
2019-02-20 13:37:00 +00:00
|
|
|
if (getPlayerPtr().getCell()->isExterior())
|
|
|
|
mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3());
|
2020-05-07 13:37:00 +00:00
|
|
|
mWorldScene->preloadCell(getPlayerPtr().getCell(), true);
|
2017-06-30 00:51:26 +02:00
|
|
|
}
|
2017-02-09 03:47:36 +01:00
|
|
|
break;
|
2014-09-21 13:37:20 +02:00
|
|
|
default:
|
|
|
|
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
|
2022-12-15 20:56:17 +01:00
|
|
|
&& !mWeatherManager->readRecord(reader, type)
|
|
|
|
&& !mWorldModel.readRecord(reader, type, contentFileMap)
|
2015-04-01 17:02:15 +02:00
|
|
|
&& !mProjectileManager->readRecord(reader, type))
|
2014-09-21 13:37:20 +02:00
|
|
|
{
|
|
|
|
throw std::runtime_error("unknown record in saved game");
|
|
|
|
}
|
|
|
|
break;
|
2013-12-07 13:17:28 +01:00
|
|
|
}
|
|
|
|
}
|
2012-01-29 17:50:51 -05:00
|
|
|
|
2013-05-01 12:47:50 +02:00
|
|
|
void World::ensureNeededRecords()
|
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
for (const auto& [id, value] : generateDefaultGameSettings())
|
2019-01-02 13:49:04 +03:00
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
if (mStore.get<ESM::GameSetting>().search(id) == nullptr)
|
2019-01-02 13:49:04 +03:00
|
|
|
{
|
|
|
|
ESM::GameSetting record;
|
2023-02-07 23:46:15 +01:00
|
|
|
record.mId = ESM::RefId::stringRefId(id);
|
|
|
|
record.mValue = value;
|
2022-07-05 17:19:47 +04:00
|
|
|
record.mRecordFlags = 0;
|
2019-01-02 13:49:04 +03:00
|
|
|
mStore.insertStatic(record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 23:46:15 +01:00
|
|
|
for (const auto& [name, value] : generateDefaultGlobals())
|
2019-01-02 13:49:04 +03:00
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
if (mStore.get<ESM::Global>().search(ESM::RefId::stringRefId(name.getValue())) == nullptr)
|
2019-01-02 13:49:04 +03:00
|
|
|
{
|
|
|
|
ESM::Global record;
|
2023-02-07 23:46:15 +01:00
|
|
|
record.mId = ESM::RefId::stringRefId(name.getValue());
|
|
|
|
record.mValue = value;
|
2022-07-02 23:25:51 +04:00
|
|
|
record.mRecordFlags = 0;
|
2019-01-02 13:49:04 +03:00
|
|
|
mStore.insertStatic(record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 23:46:15 +01:00
|
|
|
for (const auto& [id, model] : generateDefaultStatics())
|
2013-05-01 12:47:50 +02:00
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
if (mStore.get<ESM::Static>().search(ESM::RefId::stringRefId(id)) == nullptr)
|
2014-08-13 17:32:52 +02:00
|
|
|
{
|
2019-01-02 13:49:04 +03:00
|
|
|
ESM::Static record;
|
2023-02-07 23:46:15 +01:00
|
|
|
record.mId = ESM::RefId::stringRefId(id);
|
|
|
|
record.mModel = model;
|
2022-07-02 23:25:51 +04:00
|
|
|
record.mRecordFlags = 0;
|
2019-01-02 13:49:04 +03:00
|
|
|
mStore.insertStatic(record);
|
2014-08-13 17:32:52 +02:00
|
|
|
}
|
2013-05-01 12:47:50 +02:00
|
|
|
}
|
2014-08-13 17:32:52 +02:00
|
|
|
|
2023-02-07 23:46:15 +01:00
|
|
|
for (const auto& [id, model] : generateDefaultDoors())
|
2013-05-01 12:47:50 +02:00
|
|
|
{
|
2023-02-07 23:46:15 +01:00
|
|
|
if (mStore.get<ESM::Door>().search(ESM::RefId::stringRefId(id)) == nullptr)
|
2014-08-13 17:32:52 +02:00
|
|
|
{
|
2019-01-02 13:49:04 +03:00
|
|
|
ESM::Door record;
|
2023-02-07 23:46:15 +01:00
|
|
|
record.mId = ESM::RefId::stringRefId(id);
|
|
|
|
record.mModel = model;
|
2022-07-02 23:25:51 +04:00
|
|
|
record.mRecordFlags = 0;
|
2019-01-02 13:49:04 +03:00
|
|
|
mStore.insertStatic(record);
|
2014-08-13 17:32:52 +02:00
|
|
|
}
|
2013-05-01 12:47:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-02 09:38:22 +02:00
|
|
|
World::~World()
|
|
|
|
{
|
2014-06-03 00:44:32 +02:00
|
|
|
// Must be cleared before mRendering is destroyed
|
|
|
|
mProjectileManager->clear();
|
2010-07-02 09:38:22 +02:00
|
|
|
}
|
2011-08-01 14:41:15 +02:00
|
|
|
|
2022-03-17 14:30:00 +02:00
|
|
|
void World::setRandomSeed(uint32_t seed)
|
|
|
|
{
|
|
|
|
mRandomSeed = seed;
|
|
|
|
}
|
|
|
|
|
2013-12-16 15:40:47 +02:00
|
|
|
void World::useDeathCamera()
|
2013-12-15 18:50:25 +02:00
|
|
|
{
|
2021-06-20 00:57:41 +02:00
|
|
|
mRendering->getCamera()->setMode(MWRender::Camera::Mode::ThirdPerson);
|
2013-12-15 18:50:25 +02:00
|
|
|
}
|
|
|
|
|
2011-01-04 15:58:22 +01:00
|
|
|
MWWorld::Player& World::getPlayer()
|
2010-07-02 09:38:22 +02:00
|
|
|
{
|
2011-01-04 15:58:22 +01:00
|
|
|
return *mPlayer;
|
2010-07-02 09:38:22 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2022-09-03 18:50:38 +02:00
|
|
|
const std::vector<int>& World::getESMVersions() const
|
|
|
|
{
|
|
|
|
return mESMVersions;
|
|
|
|
}
|
|
|
|
|
2012-10-01 19:17:04 +04:00
|
|
|
const MWWorld::ESMStore& World::getStore() const
|
2010-07-02 16:18:25 +02:00
|
|
|
{
|
|
|
|
return mStore;
|
|
|
|
}
|
2011-08-09 09:56:09 +02:00
|
|
|
|
2011-10-06 12:29:59 +02:00
|
|
|
LocalScripts& World::getLocalScripts()
|
2010-07-02 16:18:25 +02:00
|
|
|
{
|
|
|
|
return mLocalScripts;
|
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
void World::setGlobalInt(GlobalVariableName name, int value)
|
2010-07-04 13:33:33 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
bool dateUpdated = mCurrentDate->updateGlobalInt(name, value);
|
|
|
|
if (dateUpdated)
|
|
|
|
updateSkyDate();
|
|
|
|
|
|
|
|
mGlobalVariables[name].setInteger(value);
|
2010-07-04 13:33:33 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
void World::setGlobalFloat(GlobalVariableName name, float value)
|
2011-04-21 11:00:00 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value);
|
|
|
|
if (dateUpdated)
|
|
|
|
updateSkyDate();
|
|
|
|
|
|
|
|
mGlobalVariables[name].setFloat(value);
|
2011-04-21 11:00:00 +02:00
|
|
|
}
|
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
int World::getGlobalInt(GlobalVariableName name) const
|
2010-07-21 15:01:35 +02:00
|
|
|
{
|
2013-12-10 15:09:58 +01:00
|
|
|
return mGlobalVariables[name].getInteger();
|
2010-08-03 20:01:52 +02:00
|
|
|
}
|
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
float World::getGlobalFloat(GlobalVariableName name) const
|
2012-12-20 23:16:34 +00:00
|
|
|
{
|
2013-12-10 15:09:58 +01:00
|
|
|
return mGlobalVariables[name].getFloat();
|
2012-12-20 23:16:34 +00:00
|
|
|
}
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
char World::getGlobalVariableType(GlobalVariableName name) const
|
2012-12-21 18:09:31 +00:00
|
|
|
{
|
2013-12-10 15:09:58 +01:00
|
|
|
return mGlobalVariables.getType(name);
|
2010-08-03 20:01:52 +02:00
|
|
|
}
|
2012-12-21 18:09:31 +00:00
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
std::string_view World::getMonthName(int month) const
|
2020-06-03 11:32:28 +04:00
|
|
|
{
|
|
|
|
return mCurrentDate->getMonthName(month);
|
|
|
|
}
|
|
|
|
|
2022-12-01 22:09:30 +01:00
|
|
|
std::string_view World::getCellName(const MWWorld::CellStore* cell) const
|
2012-12-21 18:09:31 +00:00
|
|
|
{
|
2013-11-26 11:39:58 +01:00
|
|
|
if (!cell)
|
|
|
|
cell = mWorldScene->getCurrentCell();
|
2023-01-27 01:13:17 +01:00
|
|
|
return getCellName(*cell->getCell());
|
2021-02-10 22:13:04 +01:00
|
|
|
}
|
2012-12-21 18:09:31 +00:00
|
|
|
|
2023-01-27 01:13:17 +01:00
|
|
|
std::string_view World::getCellName(const MWWorld::Cell& cell) const
|
2021-02-10 22:13:04 +01:00
|
|
|
{
|
2023-01-28 12:07:47 +01:00
|
|
|
if (!cell.isExterior() || !cell.getNameId().empty())
|
|
|
|
return cell.getNameId();
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2023-02-04 18:45:53 +01:00
|
|
|
return ESM::visit(ESM::VisitOverload{
|
|
|
|
[&](const ESM::Cell& cellIn) -> std::string_view { return getCellName(&cellIn); },
|
|
|
|
[&](const ESM4::Cell& cellIn) -> std::string_view {
|
|
|
|
return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
cell);
|
2021-02-10 22:13:04 +01:00
|
|
|
}
|
2012-12-21 18:09:31 +00:00
|
|
|
|
2022-12-01 22:09:30 +01:00
|
|
|
std::string_view World::getCellName(const ESM::Cell* cell) const
|
2021-02-10 22:13:04 +01:00
|
|
|
{
|
|
|
|
if (cell)
|
|
|
|
{
|
|
|
|
if (!cell->isExterior() || !cell->mName.empty())
|
2023-01-19 17:31:45 +01:00
|
|
|
return cell->mName;
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2021-02-10 22:13:04 +01:00
|
|
|
if (const ESM::Region* region = mStore.get<ESM::Region>().search(cell->mRegion))
|
2022-12-01 22:09:30 +01:00
|
|
|
return region->mName;
|
2021-02-10 22:13:04 +01:00
|
|
|
}
|
2022-12-01 22:09:30 +01:00
|
|
|
return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString();
|
2012-12-21 18:09:31 +00:00
|
|
|
}
|
2012-12-20 23:16:34 +00:00
|
|
|
|
2013-01-13 17:05:12 +00:00
|
|
|
void World::removeRefScript(MWWorld::RefData* ref)
|
|
|
|
{
|
|
|
|
mLocalScripts.remove(ref);
|
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
Ptr World::searchPtr(const ESM::RefId& name, bool activeOnly, bool searchInContainers)
|
2010-07-05 12:09:04 +02:00
|
|
|
{
|
2014-01-11 03:33:17 +01:00
|
|
|
Ptr ret;
|
2010-07-05 12:09:04 +02:00
|
|
|
// the player is always in an active cell.
|
2022-10-18 09:26:55 +02:00
|
|
|
if (name == "Player")
|
2010-07-05 12:09:04 +02:00
|
|
|
{
|
2011-01-04 15:58:22 +01:00
|
|
|
return mPlayer->getPlayer();
|
2010-07-05 12:09:04 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2010-07-05 12:09:04 +02:00
|
|
|
{
|
2014-09-07 19:09:23 +02:00
|
|
|
// TODO: caching still doesn't work efficiently here (only works for the one CellStore that the reference is
|
|
|
|
// in)
|
2023-02-19 12:47:33 +01:00
|
|
|
Ptr ptr = mWorldModel.getPtr(name, *cellstore);
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2010-07-05 12:09:04 +02:00
|
|
|
if (!ptr.isEmpty())
|
2010-07-10 13:19:04 +02:00
|
|
|
return ptr;
|
2010-07-05 12:09:04 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2010-07-05 12:09:04 +02:00
|
|
|
if (!activeOnly)
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
ret = mWorldModel.getPtr(name);
|
2014-09-07 19:09:23 +02:00
|
|
|
if (!ret.isEmpty())
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-05-06 23:06:13 +04:00
|
|
|
if (searchInContainers)
|
2014-09-07 19:09:23 +02:00
|
|
|
{
|
2019-05-06 23:06:13 +04:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
Ptr ptr = cellstore->searchInContainer(name);
|
2019-05-06 23:06:13 +04:00
|
|
|
if (!ptr.isEmpty())
|
|
|
|
return ptr;
|
|
|
|
}
|
2010-07-05 12:09:04 +02:00
|
|
|
}
|
2014-09-07 19:09:23 +02:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
Ptr ptr = mPlayer->getPlayer().getClass().getContainerStore(mPlayer->getPlayer()).search(name);
|
2014-09-07 19:09:23 +02:00
|
|
|
|
|
|
|
return ptr;
|
2014-01-11 03:33:17 +01:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
Ptr World::getPtr(const ESM::RefId& name, bool activeOnly)
|
2014-01-11 03:33:17 +01:00
|
|
|
{
|
|
|
|
Ptr ret = searchPtr(name, activeOnly);
|
|
|
|
if (!ret.isEmpty())
|
|
|
|
return ret;
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
std::string error = "failed to find an instance of object '" + name.getRefIdString() + "'";
|
2020-05-22 14:04:26 +03:00
|
|
|
if (activeOnly)
|
|
|
|
error += " in active cells";
|
|
|
|
throw std::runtime_error(error);
|
2010-07-05 12:09:04 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2014-04-29 15:27:49 +02:00
|
|
|
Ptr World::searchPtrViaActorId(int actorId)
|
|
|
|
{
|
2014-05-15 02:36:30 +02:00
|
|
|
// The player is not registered in any CellStore so must be checked manually
|
|
|
|
if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId())
|
|
|
|
return getPlayerPtr();
|
|
|
|
// Now search cells
|
2014-04-29 15:27:49 +02:00
|
|
|
return mWorldScene->searchPtrViaActorId(actorId);
|
2010-08-03 20:01:52 +02:00
|
|
|
}
|
|
|
|
|
2015-12-06 18:13:04 +01:00
|
|
|
struct FindContainerVisitor
|
2014-10-24 18:49:38 +02:00
|
|
|
{
|
2015-12-18 17:56:48 +01:00
|
|
|
ConstPtr mContainedPtr;
|
2014-10-24 18:49:38 +02:00
|
|
|
Ptr mResult;
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
FindContainerVisitor(const ConstPtr& containedPtr)
|
|
|
|
: mContainedPtr(containedPtr)
|
|
|
|
{
|
|
|
|
}
|
2014-10-24 18:49:38 +02:00
|
|
|
|
2021-06-23 23:13:59 +02:00
|
|
|
bool operator()(const Ptr& ptr)
|
2014-10-24 18:49:38 +02:00
|
|
|
{
|
|
|
|
if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr))
|
|
|
|
{
|
|
|
|
mResult = ptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
Ptr World::findContainer(const ConstPtr& ptr)
|
2014-10-24 18:49:38 +02:00
|
|
|
{
|
|
|
|
if (ptr.isInCell())
|
|
|
|
return Ptr();
|
|
|
|
|
|
|
|
Ptr player = getPlayerPtr();
|
|
|
|
if (ptr.getContainerStore() == &player.getClass().getContainerStore(player))
|
|
|
|
return player;
|
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2014-10-24 18:49:38 +02:00
|
|
|
{
|
2015-12-06 18:13:04 +01:00
|
|
|
FindContainerVisitor visitor(ptr);
|
2018-12-30 01:27:16 +03:00
|
|
|
cellstore->forEachType<ESM::Container>(visitor);
|
2015-12-07 03:47:40 +01:00
|
|
|
if (visitor.mResult.isEmpty())
|
2018-12-30 01:27:16 +03:00
|
|
|
cellstore->forEachType<ESM::Creature>(visitor);
|
2015-12-07 03:47:40 +01:00
|
|
|
if (visitor.mResult.isEmpty())
|
2018-12-30 01:27:16 +03:00
|
|
|
cellstore->forEachType<ESM::NPC>(visitor);
|
2014-10-24 18:49:38 +02:00
|
|
|
|
2015-12-06 18:13:04 +01:00
|
|
|
if (!visitor.mResult.isEmpty())
|
|
|
|
return visitor.mResult;
|
2014-10-24 18:49:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ptr();
|
|
|
|
}
|
|
|
|
|
2013-12-05 13:21:26 +01:00
|
|
|
void World::addContainerScripts(const Ptr& reference, CellStore* cell)
|
2013-01-20 16:38:56 +00:00
|
|
|
{
|
2021-10-11 11:46:21 +00:00
|
|
|
if (reference.getType() == ESM::Container::sRecordId || reference.getType() == ESM::NPC::sRecordId
|
|
|
|
|| reference.getType() == ESM::Creature::sRecordId)
|
2013-01-20 16:38:56 +00:00
|
|
|
{
|
2014-05-22 20:37:22 +02:00
|
|
|
MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);
|
2013-01-20 16:38:56 +00:00
|
|
|
for (MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
|
|
|
|
{
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = it->getClass().getScript(*it);
|
2022-08-11 22:51:55 +02:00
|
|
|
if (!script.empty())
|
2013-01-20 16:38:56 +00:00
|
|
|
{
|
|
|
|
MWWorld::Ptr item = *it;
|
|
|
|
item.mCell = cell;
|
|
|
|
mLocalScripts.add(script, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-03 01:06:38 +02:00
|
|
|
void World::enable(const Ptr& reference)
|
2010-07-09 16:07:03 +02:00
|
|
|
{
|
2014-02-13 20:24:27 +01:00
|
|
|
if (!reference.isInCell())
|
|
|
|
return;
|
|
|
|
|
2010-07-10 13:19:04 +02:00
|
|
|
if (!reference.getRefData().isEnabled())
|
2010-07-09 16:07:03 +02:00
|
|
|
{
|
2010-07-10 13:19:04 +02:00
|
|
|
reference.getRefData().enable();
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2012-05-25 17:28:27 +02:00
|
|
|
if (mWorldScene->getActiveCells().find(reference.getCell()) != mWorldScene->getActiveCells().end()
|
|
|
|
&& reference.getRefData().getCount())
|
|
|
|
mWorldScene->addObjectToScene(reference);
|
2019-08-03 13:37:00 +00:00
|
|
|
|
|
|
|
if (reference.getCellRef().getRefNum().hasContentFile())
|
2020-05-08 13:37:00 +00:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
int type = mStore.find(reference.getCellRef().getRefId());
|
2020-05-08 13:37:00 +00:00
|
|
|
if (mRendering->pagingEnableObject(type, reference, true))
|
|
|
|
mWorldScene->reloadTerrain();
|
2020-05-08 13:37:00 +00:00
|
|
|
}
|
2010-07-09 16:07:03 +02:00
|
|
|
}
|
|
|
|
}
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2013-01-20 16:38:56 +00:00
|
|
|
void World::removeContainerScripts(const Ptr& reference)
|
|
|
|
{
|
2021-10-11 11:46:21 +00:00
|
|
|
if (reference.getType() == ESM::Container::sRecordId || reference.getType() == ESM::NPC::sRecordId
|
|
|
|
|| reference.getType() == ESM::Creature::sRecordId)
|
2013-01-20 16:38:56 +00:00
|
|
|
{
|
2014-05-22 20:37:22 +02:00
|
|
|
MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);
|
2013-01-20 16:38:56 +00:00
|
|
|
for (MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
|
2013-01-20 15:43:52 +00:00
|
|
|
{
|
2022-11-13 12:04:57 +01:00
|
|
|
const ESM::RefId& script = it->getClass().getScript(*it);
|
2022-08-11 22:51:55 +02:00
|
|
|
if (!script.empty())
|
2013-01-20 15:43:52 +00:00
|
|
|
{
|
2013-01-20 16:38:56 +00:00
|
|
|
MWWorld::Ptr item = *it;
|
|
|
|
mLocalScripts.remove(item);
|
2013-01-20 15:43:52 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-09 16:07:03 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2012-07-03 01:06:38 +02:00
|
|
|
void World::disable(const Ptr& reference)
|
2010-07-09 16:07:03 +02:00
|
|
|
{
|
2019-08-03 13:37:00 +00:00
|
|
|
if (!reference.getRefData().isEnabled())
|
|
|
|
return;
|
|
|
|
|
2014-02-13 20:24:27 +01:00
|
|
|
// disable is a no-op for items in containers
|
|
|
|
if (!reference.isInCell())
|
|
|
|
return;
|
|
|
|
|
2019-08-03 13:37:00 +00:00
|
|
|
if (reference == getPlayerPtr())
|
|
|
|
throw std::runtime_error("can not disable player object");
|
2015-11-03 17:53:57 +01:00
|
|
|
|
2019-08-03 13:37:00 +00:00
|
|
|
reference.getRefData().disable();
|
2013-02-05 12:19:06 +01:00
|
|
|
|
2019-08-03 13:37:00 +00:00
|
|
|
if (reference.getCellRef().getRefNum().hasContentFile())
|
2020-05-08 13:37:00 +00:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
int type = mStore.find(reference.getCellRef().getRefId());
|
2020-05-08 13:37:00 +00:00
|
|
|
if (mRendering->pagingEnableObject(type, reference, false))
|
|
|
|
mWorldScene->reloadTerrain();
|
2010-08-03 20:01:52 +02:00
|
|
|
}
|
2019-08-03 13:37:00 +00:00
|
|
|
|
|
|
|
if (mWorldScene->getActiveCells().find(reference.getCell()) != mWorldScene->getActiveCells().end()
|
|
|
|
&& reference.getRefData().getCount())
|
2022-06-19 20:16:31 +02:00
|
|
|
{
|
2019-08-03 13:37:00 +00:00
|
|
|
mWorldScene->removeObjectFromScene(reference);
|
2022-06-19 20:16:31 +02:00
|
|
|
mWorldScene->addPostponedPhysicsObjects();
|
|
|
|
}
|
2010-07-09 16:07:03 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2015-08-27 14:20:45 -05:00
|
|
|
void World::advanceTime(double hours, bool incremental)
|
2010-07-18 18:29:16 +02:00
|
|
|
{
|
2018-12-15 10:23:50 +04:00
|
|
|
if (!incremental)
|
|
|
|
{
|
|
|
|
// When we fast-forward time, we should recharge magic items
|
|
|
|
// in all loaded cells, using game world time
|
|
|
|
float duration = hours * 3600;
|
|
|
|
const float timeScaleFactor = getTimeScaleFactor();
|
|
|
|
if (timeScaleFactor != 0.0f)
|
|
|
|
duration /= timeScaleFactor;
|
|
|
|
|
|
|
|
rechargeItems(duration, false);
|
|
|
|
}
|
2013-11-19 16:42:24 +01:00
|
|
|
|
2015-08-27 14:20:45 -05:00
|
|
|
mWeatherManager->advanceTime(hours, incremental);
|
2020-06-03 11:32:28 +04:00
|
|
|
mCurrentDate->advanceTime(hours, mGlobalVariables);
|
|
|
|
updateSkyDate();
|
2012-09-25 21:28:25 +02:00
|
|
|
|
2016-07-06 00:11:58 +02:00
|
|
|
if (!incremental)
|
2017-07-28 16:50:52 +04:00
|
|
|
{
|
|
|
|
mRendering->notifyWorldSpaceChanged();
|
2016-07-06 00:11:58 +02:00
|
|
|
mProjectileManager->clear();
|
2020-11-23 06:05:45 +00:00
|
|
|
mDiscardMovements = true;
|
2017-07-28 16:50:52 +04:00
|
|
|
}
|
2010-07-22 11:48:27 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2020-06-03 11:32:28 +04:00
|
|
|
float World::getTimeScaleFactor() const
|
2012-09-18 20:53:32 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
return mCurrentDate->getTimeScaleFactor();
|
2012-09-18 20:53:32 +02:00
|
|
|
}
|
|
|
|
|
2022-07-03 12:51:28 +00:00
|
|
|
void World::setSimulationTimeScale(float scale)
|
|
|
|
{
|
|
|
|
mSimulationTimeScale = std::max(0.f, scale);
|
|
|
|
MWBase::Environment::get().getSoundManager()->setSimulationTimeScale(mSimulationTimeScale);
|
|
|
|
}
|
|
|
|
|
2020-06-03 11:32:28 +04:00
|
|
|
TimeStamp World::getTimeStamp() const
|
2012-09-18 20:53:32 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
return mCurrentDate->getTimeStamp();
|
2012-09-18 20:53:32 +02:00
|
|
|
}
|
|
|
|
|
2020-06-03 11:32:28 +04:00
|
|
|
ESM::EpochTimeStamp World::getEpochTimeStamp() const
|
2012-05-19 09:31:45 +02:00
|
|
|
{
|
2020-06-03 11:32:28 +04:00
|
|
|
return mCurrentDate->getEpochTimeStamp();
|
2012-05-19 09:31:45 +02:00
|
|
|
}
|
|
|
|
|
2011-04-26 21:38:21 +02:00
|
|
|
bool World::toggleSky()
|
2010-07-18 19:48:02 +02:00
|
|
|
{
|
2015-06-16 20:56:48 +02:00
|
|
|
mSky = !mSky;
|
|
|
|
mRendering->setSkyEnabled(mSky);
|
|
|
|
return mSky;
|
2010-07-18 19:48:02 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2010-07-18 19:48:02 +02:00
|
|
|
int World::getMasserPhase() const
|
|
|
|
{
|
2015-06-16 20:56:48 +02:00
|
|
|
return mRendering->skyGetMasserPhase();
|
2010-07-18 19:48:02 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2010-07-18 19:48:02 +02:00
|
|
|
int World::getSecundaPhase() const
|
|
|
|
{
|
2015-06-16 20:56:48 +02:00
|
|
|
return mRendering->skyGetSecundaPhase();
|
2010-07-18 19:48:02 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2010-07-18 19:48:02 +02:00
|
|
|
void World::setMoonColour(bool red)
|
|
|
|
{
|
2015-06-16 20:56:48 +02:00
|
|
|
mRendering->skySetMoonColour(red);
|
2010-07-18 19:48:02 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2016-03-24 17:18:08 +01:00
|
|
|
void World::changeToInteriorCell(
|
2023-01-19 17:31:45 +01:00
|
|
|
const std::string_view cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
2010-07-22 12:29:23 +02:00
|
|
|
{
|
2015-05-23 06:07:11 +02:00
|
|
|
mPhysics->clearQueuedMovement();
|
Process movement queue in one or several background threads
Before movement calculation, the main thread prepare a
vector of ActorFrameData, which contains all data necessary to perform
the simulation, and feed it to the solver. At the same time it fetches
the result from the previous background simulation, which in turn is
used by the game mechanics.
Other functions of the physics system (weapon hit for instance)
interrupt the background simulation, with some exceptions described
below.
The number of threads is controlled by the numeric setting
[Physics]
async num threads
In case 'async num threads' > 1 and Bullet doesn't support multiple threads,
1 async thread will be used. 0 means synchronous solver.
Additional settings (will be silently switched off if async num threads = 0)
[Physics]
defer aabb update
Update AABBs of actors and objects in the background thread(s). It is not an especially
costly operation, but it needs exclusive access to the collision world, which blocks
other operations. Since AABB needs to be updated for collision detection, one can queue
them to defer update before start of the movement solver. Extensive tests on as much
as one installation (mine) show no drawback having that switched on.
[Physics]
lineofsight keep inactive cache
Control for how long (how many frames) the line of sight (LOS) request will be kept updated.
When a request for LOS is made for the first time, the background threads are stopped to
service it. From now on, the LOS will be refreshed preemptively as part of the background
routine until it is not required for lineofsight keep inactive cache frames. This mean
that subsequent request will not interrupt the background computation.
2020-10-15 06:11:44 +02:00
|
|
|
mDiscardMovements = true;
|
2014-08-13 16:23:34 +02:00
|
|
|
|
2022-12-01 19:37:35 +01:00
|
|
|
if (changeEvent && mCurrentWorldSpace != cellName)
|
2014-05-17 05:21:17 +02:00
|
|
|
{
|
|
|
|
// changed worldspace
|
|
|
|
mProjectileManager->clear();
|
2015-04-19 17:55:56 +02:00
|
|
|
mRendering->notifyWorldSpaceChanged();
|
|
|
|
|
2014-05-17 05:21:17 +02:00
|
|
|
mCurrentWorldSpace = cellName;
|
|
|
|
}
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2014-01-08 18:39:44 +01:00
|
|
|
removeContainerScripts(getPlayerPtr());
|
2016-03-24 17:18:08 +01:00
|
|
|
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
|
2014-01-08 18:39:44 +01:00
|
|
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
2020-07-18 13:27:53 +02:00
|
|
|
mRendering->getCamera()->instantTransition();
|
2010-07-22 12:29:23 +02:00
|
|
|
}
|
2010-08-03 20:01:52 +02:00
|
|
|
|
2016-03-24 17:18:08 +01:00
|
|
|
void World::changeToExteriorCell(const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
2010-08-20 13:33:03 +02:00
|
|
|
{
|
2015-05-23 06:07:11 +02:00
|
|
|
mPhysics->clearQueuedMovement();
|
Process movement queue in one or several background threads
Before movement calculation, the main thread prepare a
vector of ActorFrameData, which contains all data necessary to perform
the simulation, and feed it to the solver. At the same time it fetches
the result from the previous background simulation, which in turn is
used by the game mechanics.
Other functions of the physics system (weapon hit for instance)
interrupt the background simulation, with some exceptions described
below.
The number of threads is controlled by the numeric setting
[Physics]
async num threads
In case 'async num threads' > 1 and Bullet doesn't support multiple threads,
1 async thread will be used. 0 means synchronous solver.
Additional settings (will be silently switched off if async num threads = 0)
[Physics]
defer aabb update
Update AABBs of actors and objects in the background thread(s). It is not an especially
costly operation, but it needs exclusive access to the collision world, which blocks
other operations. Since AABB needs to be updated for collision detection, one can queue
them to defer update before start of the movement solver. Extensive tests on as much
as one installation (mine) show no drawback having that switched on.
[Physics]
lineofsight keep inactive cache
Control for how long (how many frames) the line of sight (LOS) request will be kept updated.
When a request for LOS is made for the first time, the background threads are stopped to
service it. From now on, the LOS will be refreshed preemptively as part of the background
routine until it is not required for lineofsight keep inactive cache frames. This mean
that subsequent request will not interrupt the background computation.
2020-10-15 06:11:44 +02:00
|
|
|
mDiscardMovements = true;
|
2014-08-13 16:23:34 +02:00
|
|
|
|
2016-07-02 19:48:11 +02:00
|
|
|
if (changeEvent && mCurrentWorldSpace != ESM::CellId::sDefaultWorldspace)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
|
|
|
// changed worldspace
|
|
|
|
mProjectileManager->clear();
|
2015-04-19 17:55:56 +02:00
|
|
|
mRendering->notifyWorldSpaceChanged();
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|
2014-01-08 18:39:44 +01:00
|
|
|
removeContainerScripts(getPlayerPtr());
|
2016-03-24 17:18:08 +01:00
|
|
|
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
|
2014-01-08 18:39:44 +01:00
|
|
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
2020-07-18 13:27:53 +02:00
|
|
|
mRendering->getCamera()->instantTransition();
|
2010-08-20 13:33:03 +02:00
|
|
|
}
|
|
|
|
|
2016-03-24 17:18:08 +01:00
|
|
|
void World::changeToCell(
|
|
|
|
const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
2014-01-16 12:03:23 +01:00
|
|
|
{
|
2016-02-27 12:53:07 +01:00
|
|
|
if (!changeEvent)
|
2014-05-17 05:21:17 +02:00
|
|
|
mCurrentWorldSpace = cellId.mWorldspace;
|
|
|
|
|
2014-01-16 12:03:23 +01:00
|
|
|
if (cellId.mPaged)
|
2016-03-24 17:18:08 +01:00
|
|
|
changeToExteriorCell(position, adjustPlayerPos, changeEvent);
|
2014-01-16 12:03:23 +01:00
|
|
|
else
|
2016-05-08 18:59:41 +02:00
|
|
|
changeToInteriorCell(cellId.mWorldspace, position, adjustPlayerPos, changeEvent);
|
2020-06-03 11:32:28 +04:00
|
|
|
|
|
|
|
mCurrentDate->setup(mGlobalVariables);
|
2014-01-16 12:03:23 +01:00
|
|
|
}
|
|
|
|
|
2022-05-06 21:03:02 +02:00
|
|
|
float World::getMaxActivationDistance() const
|
2010-08-05 13:29:49 +02:00
|
|
|
{
|
2013-01-08 19:52:18 -08:00
|
|
|
if (mActivationDistanceOverride >= 0)
|
2015-03-08 13:07:29 +13:00
|
|
|
return static_cast<float>(mActivationDistanceOverride);
|
2013-01-08 19:52:18 -08:00
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
static const int iMaxActivateDist
|
|
|
|
= mStore.get<ESM::GameSetting>().find("iMaxActivateDist")->mValue.getInteger();
|
2016-06-07 14:52:44 +02:00
|
|
|
return static_cast<float>(iMaxActivateDist);
|
2013-01-07 23:40:17 -08:00
|
|
|
}
|
|
|
|
|
2013-01-07 23:27:37 -08:00
|
|
|
MWWorld::Ptr World::getFacedObject()
|
2010-08-05 13:29:49 +02:00
|
|
|
{
|
2015-05-24 03:36:34 +02:00
|
|
|
MWWorld::Ptr facedObject;
|
2014-09-09 17:50:56 +02:00
|
|
|
|
|
|
|
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
|
|
|
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
2015-05-24 03:36:34 +02:00
|
|
|
facedObject = getFacedObject(getMaxActivationDistance() * 50, false);
|
2014-09-09 17:50:56 +02:00
|
|
|
else
|
|
|
|
{
|
2016-09-23 00:16:51 +09:00
|
|
|
float activationDistance = getActivationDistancePlusTelekinesis();
|
2014-09-09 17:50:56 +02:00
|
|
|
|
2016-07-09 02:12:58 +09:00
|
|
|
facedObject = getFacedObject(activationDistance, true);
|
2014-09-09 17:50:56 +02:00
|
|
|
|
2016-07-07 00:03:14 +09:00
|
|
|
if (!facedObject.isEmpty() && !facedObject.getClass().allowTelekinesis(facedObject)
|
2017-03-22 19:55:48 +09:00
|
|
|
&& mDistanceToFacedObject > getMaxActivationDistance()
|
|
|
|
&& !MWBase::Environment::get().getWindowManager()->isGuiMode())
|
2020-11-13 11:39:47 +04:00
|
|
|
return nullptr;
|
2014-09-09 17:50:56 +02:00
|
|
|
}
|
2015-05-24 03:36:34 +02:00
|
|
|
return facedObject;
|
2010-08-05 13:29:49 +02:00
|
|
|
}
|
2010-08-07 20:25:17 +02:00
|
|
|
|
2016-07-08 23:07:07 +09:00
|
|
|
float World::getDistanceToFacedObject()
|
|
|
|
{
|
|
|
|
return mDistanceToFacedObject;
|
|
|
|
}
|
|
|
|
|
2015-12-18 17:36:14 +01:00
|
|
|
osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const
|
2013-07-25 12:58:43 -07:00
|
|
|
{
|
2015-12-18 17:36:14 +01:00
|
|
|
const MWRender::Animation* anim = mRendering->getAnimation(actor);
|
2015-12-10 17:48:45 -08:00
|
|
|
if (anim)
|
2013-07-25 12:58:43 -07:00
|
|
|
{
|
2015-12-10 17:48:45 -08:00
|
|
|
const osg::Node* node = anim->getNode("Head");
|
|
|
|
if (!node)
|
|
|
|
node = anim->getNode("Bip01 Head");
|
|
|
|
if (node)
|
2015-05-22 04:36:17 +02:00
|
|
|
{
|
2016-02-22 18:58:19 +01:00
|
|
|
osg::NodePathList nodepaths = node->getParentalNodePaths();
|
|
|
|
if (!nodepaths.empty())
|
|
|
|
return osg::computeLocalToWorld(nodepaths[0]);
|
2015-05-22 04:36:17 +02:00
|
|
|
}
|
2013-07-25 12:58:43 -07:00
|
|
|
}
|
2015-12-10 17:48:45 -08:00
|
|
|
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
2015-09-17 01:08:16 +02:00
|
|
|
}
|
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
std::pair<MWWorld::Ptr, osg::Vec3f> World::getHitContact(
|
|
|
|
const MWWorld::ConstPtr& ptr, float distance, std::vector<MWWorld::Ptr>& targets)
|
2015-09-17 01:08:16 +02:00
|
|
|
{
|
|
|
|
const ESM::Position& posdata = ptr.getRefData().getPosition();
|
|
|
|
|
|
|
|
osg::Quat rot
|
|
|
|
= osg::Quat(posdata.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(posdata.rot[2], osg::Vec3f(0, 0, -1));
|
2016-06-10 23:30:41 +02:00
|
|
|
|
2018-05-09 10:31:53 +04:00
|
|
|
osg::Vec3f halfExtents = mPhysics->getHalfExtents(ptr);
|
2016-06-10 23:30:41 +02:00
|
|
|
|
2018-05-09 12:53:58 +04:00
|
|
|
// the origin of hitbox is an actor's front, not center
|
|
|
|
distance += halfExtents.y();
|
|
|
|
|
|
|
|
// special cased for better aiming with the camera
|
|
|
|
// if we do not hit anything, will use the default approach as fallback
|
2016-06-10 23:30:41 +02:00
|
|
|
if (ptr == getPlayerPtr())
|
|
|
|
{
|
2018-05-09 12:53:58 +04:00
|
|
|
osg::Vec3f pos = getActorHeadTransform(ptr).getTrans();
|
|
|
|
|
|
|
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
|
|
|
if (!result.first.isEmpty())
|
|
|
|
return std::make_pair(result.first, result.second);
|
2016-06-10 23:30:41 +02:00
|
|
|
}
|
2013-07-25 12:58:43 -07:00
|
|
|
|
2018-05-09 12:53:58 +04:00
|
|
|
osg::Vec3f pos = ptr.getRefData().getPosition().asVec3();
|
|
|
|
|
|
|
|
// general case, compatible with all types of different creatures
|
|
|
|
// note: we intentionally do *not* use the collision box offset here, this is required to make
|
|
|
|
// some flying creatures work that have their collision box offset in the air
|
|
|
|
pos.z() += halfExtents.z();
|
2018-05-09 10:31:53 +04:00
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = mPhysics->getHitContact(ptr, pos, rot, distance, targets);
|
2015-05-22 04:36:17 +02:00
|
|
|
if (result.first.isEmpty())
|
|
|
|
return std::make_pair(MWWorld::Ptr(), osg::Vec3f());
|
2013-07-25 12:58:43 -07:00
|
|
|
|
2015-05-22 04:36:17 +02:00
|
|
|
return std::make_pair(result.first, result.second);
|
2013-07-25 12:58:43 -07:00
|
|
|
}
|
|
|
|
|
2012-07-03 01:06:38 +02:00
|
|
|
void World::deleteObject(const Ptr& ptr)
|
2010-08-07 20:25:17 +02:00
|
|
|
{
|
2018-10-09 10:21:12 +04:00
|
|
|
if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr)
|
2010-08-07 20:25:17 +02:00
|
|
|
{
|
2015-12-09 00:26:39 +01:00
|
|
|
if (ptr == getPlayerPtr())
|
|
|
|
throw std::runtime_error("can not delete player object");
|
|
|
|
|
2013-10-16 18:39:29 +02:00
|
|
|
ptr.getRefData().setCount(0);
|
2010-08-07 20:25:17 +02:00
|
|
|
|
2013-10-16 18:39:29 +02:00
|
|
|
if (ptr.isInCell()
|
|
|
|
&& mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()
|
|
|
|
&& ptr.getRefData().isEnabled())
|
2012-05-24 13:26:07 +02:00
|
|
|
{
|
2012-05-25 17:28:27 +02:00
|
|
|
mWorldScene->removeObjectFromScene(ptr);
|
2012-05-24 13:26:07 +02:00
|
|
|
mLocalScripts.remove(ptr);
|
2013-01-20 17:01:30 +00:00
|
|
|
removeContainerScripts(ptr);
|
2012-05-24 13:26:07 +02:00
|
|
|
}
|
2010-08-07 20:25:17 +02:00
|
|
|
}
|
|
|
|
}
|
2010-08-21 12:31:04 +02:00
|
|
|
|
2014-12-06 21:08:18 +01:00
|
|
|
void World::undeleteObject(const Ptr& ptr)
|
|
|
|
{
|
2015-01-11 12:20:22 +13:00
|
|
|
if (!ptr.getCellRef().hasContentFile())
|
2014-12-06 21:08:18 +01:00
|
|
|
return;
|
|
|
|
if (ptr.getRefData().isDeleted())
|
|
|
|
{
|
|
|
|
ptr.getRefData().setCount(1);
|
|
|
|
if (mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()
|
|
|
|
&& ptr.getRefData().isEnabled())
|
|
|
|
{
|
|
|
|
mWorldScene->addObjectToScene(ptr);
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = ptr.getClass().getScript(ptr);
|
2014-12-06 21:08:18 +01:00
|
|
|
if (!script.empty())
|
|
|
|
mLocalScripts.add(script, ptr);
|
|
|
|
addContainerScripts(ptr, ptr.getCell());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-28 14:36:30 +02:00
|
|
|
MWWorld::Ptr World::moveObject(
|
|
|
|
const Ptr& ptr, CellStore* newCell, const osg::Vec3f& position, bool movePhysics, bool keepActive)
|
2010-08-21 12:31:04 +02:00
|
|
|
{
|
2014-06-14 17:56:41 +02:00
|
|
|
ESM::Position pos = ptr.getRefData().getPosition();
|
2021-04-11 19:17:17 +02:00
|
|
|
std::memcpy(pos.pos, &position, sizeof(osg::Vec3f));
|
2014-06-14 17:56:41 +02:00
|
|
|
ptr.getRefData().setPosition(pos);
|
|
|
|
|
2018-09-08 18:17:11 +04:00
|
|
|
CellStore* currCell = ptr.isInCell()
|
|
|
|
? ptr.getCell()
|
|
|
|
: nullptr; // currCell == nullptr should only happen for player, during initial startup
|
2012-08-08 14:51:33 +04:00
|
|
|
bool isPlayer = ptr == mPlayer->getPlayer();
|
2014-09-28 12:44:47 +02:00
|
|
|
bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell));
|
2015-05-17 16:25:13 +02:00
|
|
|
MWWorld::Ptr newPtr = ptr;
|
2012-11-07 19:34:28 +01:00
|
|
|
|
2018-09-08 18:17:11 +04:00
|
|
|
if (!isPlayer && !currCell)
|
2022-10-18 09:26:55 +02:00
|
|
|
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
|
|
|
+ "\" to another cell: current cell is nullptr");
|
2018-09-08 18:17:11 +04:00
|
|
|
|
|
|
|
if (!newCell)
|
2022-10-18 09:26:55 +02:00
|
|
|
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
|
|
|
+ "\" to another cell: new cell is nullptr");
|
2018-09-08 18:17:11 +04:00
|
|
|
|
2014-03-07 06:11:00 +01:00
|
|
|
if (currCell != newCell)
|
2012-11-05 18:40:02 +00:00
|
|
|
{
|
2013-04-04 16:51:22 +02:00
|
|
|
removeContainerScripts(ptr);
|
2013-01-31 00:21:04 +00:00
|
|
|
|
2012-11-05 18:40:02 +00:00
|
|
|
if (isPlayer)
|
2013-01-28 23:39:11 -08:00
|
|
|
{
|
2014-03-07 06:11:00 +01:00
|
|
|
if (!newCell->isExterior())
|
2018-07-08 23:08:57 +03:00
|
|
|
{
|
2023-01-28 12:07:47 +01:00
|
|
|
changeToInteriorCell(newCell->getCell()->getNameId(), pos, false);
|
2018-07-08 23:08:57 +03:00
|
|
|
removeContainerScripts(getPlayerPtr());
|
|
|
|
}
|
2012-11-05 18:40:02 +00:00
|
|
|
else
|
|
|
|
{
|
2014-10-02 16:30:23 +02:00
|
|
|
if (mWorldScene->isCellActive(*newCell))
|
|
|
|
mWorldScene->changePlayerCell(newCell, pos, false);
|
|
|
|
else
|
|
|
|
mWorldScene->changeToExteriorCell(pos, false);
|
2012-08-08 14:51:33 +04:00
|
|
|
}
|
2014-03-07 06:11:00 +01:00
|
|
|
addContainerScripts(getPlayerPtr(), newCell);
|
2015-05-17 16:25:13 +02:00
|
|
|
newPtr = getPlayerPtr();
|
2013-01-28 23:39:11 -08:00
|
|
|
}
|
2018-09-08 18:17:11 +04:00
|
|
|
else
|
2013-01-28 23:39:11 -08:00
|
|
|
{
|
2018-08-01 19:30:30 +04:00
|
|
|
bool currCellActive = mWorldScene->isCellActive(*currCell);
|
2018-09-08 18:17:11 +04:00
|
|
|
bool newCellActive = mWorldScene->isCellActive(*newCell);
|
2014-06-24 15:29:36 +02:00
|
|
|
if (!currCellActive && newCellActive)
|
2014-06-16 17:14:35 +02:00
|
|
|
{
|
2015-12-04 19:46:02 +01:00
|
|
|
newPtr = currCell->moveTo(ptr, newCell);
|
2021-09-27 22:23:00 +02:00
|
|
|
if (newPtr.getRefData().isEnabled())
|
|
|
|
mWorldScene->addObjectToScene(newPtr);
|
2014-06-16 17:14:35 +02:00
|
|
|
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = newPtr.getClass().getScript(newPtr);
|
2018-07-08 23:08:57 +03:00
|
|
|
if (!script.empty())
|
2018-07-08 23:13:24 +03:00
|
|
|
{
|
2014-06-16 17:14:35 +02:00
|
|
|
mLocalScripts.add(script, newPtr);
|
2018-07-08 23:13:24 +03:00
|
|
|
}
|
2014-06-16 17:14:35 +02:00
|
|
|
addContainerScripts(newPtr, newCell);
|
|
|
|
}
|
2014-06-24 15:29:36 +02:00
|
|
|
else if (!newCellActive && currCellActive)
|
2012-11-05 18:40:02 +00:00
|
|
|
{
|
2021-08-28 14:36:30 +02:00
|
|
|
mWorldScene->removeObjectFromScene(ptr, keepActive);
|
2012-08-08 14:51:33 +04:00
|
|
|
mLocalScripts.remove(ptr);
|
2013-02-05 12:19:06 +01:00
|
|
|
removeContainerScripts(ptr);
|
2012-08-08 14:51:33 +04:00
|
|
|
haveToMove = false;
|
2013-11-28 17:31:17 +01:00
|
|
|
|
2015-12-04 19:46:02 +01:00
|
|
|
newPtr = currCell->moveTo(ptr, newCell);
|
2020-11-13 11:39:47 +04:00
|
|
|
newPtr.getRefData().setBaseNode(nullptr);
|
2012-11-05 18:40:02 +00:00
|
|
|
}
|
2014-06-24 15:29:36 +02:00
|
|
|
else if (!currCellActive && !newCellActive)
|
2015-12-04 19:46:02 +01:00
|
|
|
newPtr = currCell->moveTo(ptr, newCell);
|
2014-06-24 15:29:36 +02:00
|
|
|
else // both cells active
|
2012-11-05 18:40:02 +00:00
|
|
|
{
|
2015-12-04 19:46:02 +01:00
|
|
|
newPtr = currCell->moveTo(ptr, newCell);
|
2012-08-08 14:51:33 +04:00
|
|
|
|
2015-05-23 20:33:44 +02:00
|
|
|
mRendering->updatePtr(ptr, newPtr);
|
2015-05-17 16:25:13 +02:00
|
|
|
MWBase::Environment::get().getSoundManager()->updatePtr(ptr, newPtr);
|
2015-05-23 20:33:44 +02:00
|
|
|
mPhysics->updatePtr(ptr, newPtr);
|
2013-02-24 14:59:21 +04:00
|
|
|
|
2013-01-29 00:19:24 -08:00
|
|
|
MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager();
|
2015-05-17 16:25:13 +02:00
|
|
|
mechMgr->updateCell(ptr, newPtr);
|
2012-08-08 14:51:33 +04:00
|
|
|
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = ptr.getClass().getScript(ptr);
|
2013-02-05 12:19:06 +01:00
|
|
|
if (!script.empty())
|
2012-11-05 18:40:02 +00:00
|
|
|
{
|
2013-01-29 00:19:24 -08:00
|
|
|
mLocalScripts.remove(ptr);
|
2013-02-05 12:19:06 +01:00
|
|
|
removeContainerScripts(ptr);
|
2015-05-17 16:25:13 +02:00
|
|
|
mLocalScripts.add(script, newPtr);
|
|
|
|
addContainerScripts(newPtr, newCell);
|
2010-08-21 12:41:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-11 17:24:20 +04:00
|
|
|
|
|
|
|
MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);
|
2020-07-12 13:25:40 +02:00
|
|
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
|
2010-08-21 12:41:59 +02:00
|
|
|
}
|
2015-05-17 16:25:13 +02:00
|
|
|
if (haveToMove && newPtr.getRefData().getBaseNode())
|
2012-11-05 18:40:02 +00:00
|
|
|
{
|
2022-09-17 00:23:38 +02:00
|
|
|
mRendering->moveObject(newPtr, position);
|
2016-02-13 02:56:41 +01:00
|
|
|
if (movePhysics)
|
2019-01-14 00:38:34 +03:00
|
|
|
{
|
2022-09-17 00:23:38 +02:00
|
|
|
mPhysics->updatePosition(newPtr);
|
|
|
|
if (const MWPhysics::Object* object = mPhysics->getObject(newPtr))
|
2021-07-05 00:20:44 +02:00
|
|
|
updateNavigatorObject(*object);
|
2019-01-14 00:38:34 +03:00
|
|
|
}
|
2012-07-30 23:28:14 +04:00
|
|
|
}
|
2020-05-11 13:37:00 +00:00
|
|
|
|
2014-10-02 16:30:23 +02:00
|
|
|
if (isPlayer)
|
2021-04-11 18:18:10 +02:00
|
|
|
mWorldScene->playerMoved(position);
|
2020-05-11 13:37:00 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
mWorldScene->removeFromPagedRefs(newPtr);
|
2014-10-02 16:30:23 +02:00
|
|
|
}
|
2020-05-11 13:37:00 +00:00
|
|
|
|
2015-05-17 16:25:13 +02:00
|
|
|
return newPtr;
|
2012-08-08 14:51:33 +04:00
|
|
|
}
|
|
|
|
|
2021-04-11 18:18:10 +02:00
|
|
|
MWWorld::Ptr World::moveObject(const Ptr& ptr, const osg::Vec3f& position, bool movePhysics, bool moveToActive)
|
2012-08-08 14:51:33 +04:00
|
|
|
{
|
2022-05-23 19:21:44 +02:00
|
|
|
const osg::Vec2i index = positionToCellIndex(position.x(), position.y());
|
2013-04-04 16:51:22 +02:00
|
|
|
|
2019-03-31 14:02:24 +03:00
|
|
|
CellStore* cell = ptr.getCell();
|
2022-12-15 20:56:17 +01:00
|
|
|
CellStore* newCell = mWorldModel.getExterior(index.x(), index.y());
|
2019-05-05 14:02:14 +03:00
|
|
|
bool isCellActive = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior()
|
|
|
|
&& mWorldScene->isCellActive(*newCell);
|
2012-08-11 17:30:55 +02:00
|
|
|
|
2019-03-31 14:02:24 +03:00
|
|
|
if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor()))
|
|
|
|
cell = newCell;
|
2013-04-04 16:51:22 +02:00
|
|
|
|
2021-04-11 18:18:10 +02:00
|
|
|
return moveObject(ptr, cell, position, movePhysics);
|
2011-08-02 18:50:31 +02:00
|
|
|
}
|
|
|
|
|
In 0.46, SetPos was setting position of actors before physics simulation, and from this position movement was simulated. This changed with async physics merging, and at the same time problems started, mostly with abot's scenic travel.
Skipping the simulation, switching off collisions, and other approaches were not correct as they either broke some mods, or some core mechanics of the engine such as teleportation or waterwalking. As it turns out, the way to go is to simply do _nothing_ (modulo some gymnastics to account for the 1 frame difference in case of async).
Scripted movement and the unstucking logic tends to collide. Early out of unstuck in case the actor doesn't attempt to move. This means there is no AI package for NPC, which are the case for some boats and striders, or the player is content with their position.
2023-03-16 21:33:36 +01:00
|
|
|
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec, bool moveToActive)
|
2020-12-18 08:18:07 +01:00
|
|
|
{
|
|
|
|
auto* actor = mPhysics->getActor(ptr);
|
2021-04-16 16:09:15 +02:00
|
|
|
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
|
2020-12-18 08:18:07 +01:00
|
|
|
if (actor)
|
2021-10-19 18:36:30 +02:00
|
|
|
actor->adjustPosition(vec);
|
2021-04-18 08:45:43 +02:00
|
|
|
if (ptr.getClass().isActor())
|
In 0.46, SetPos was setting position of actors before physics simulation, and from this position movement was simulated. This changed with async physics merging, and at the same time problems started, mostly with abot's scenic travel.
Skipping the simulation, switching off collisions, and other approaches were not correct as they either broke some mods, or some core mechanics of the engine such as teleportation or waterwalking. As it turns out, the way to go is to simply do _nothing_ (modulo some gymnastics to account for the 1 frame difference in case of async).
Scripted movement and the unstucking logic tends to collide. Early out of unstuck in case the actor doesn't attempt to move. This means there is no AI package for NPC, which are the case for some boats and striders, or the player is content with their position.
2023-03-16 21:33:36 +01:00
|
|
|
return moveObject(ptr, newpos, false, moveToActive && ptr != getPlayerPtr());
|
2021-04-11 18:18:10 +02:00
|
|
|
return moveObject(ptr, newpos);
|
2020-12-18 08:18:07 +01:00
|
|
|
}
|
|
|
|
|
2022-02-15 17:25:07 +01:00
|
|
|
void World::scaleObject(const Ptr& ptr, float scale, bool force)
|
2012-05-25 18:23:06 +02:00
|
|
|
{
|
2022-02-15 17:25:07 +01:00
|
|
|
if (!force && scale == ptr.getCellRef().getScale())
|
2022-01-15 13:04:15 +01:00
|
|
|
return;
|
2019-03-03 15:14:51 +03:00
|
|
|
if (mPhysics->getActor(ptr))
|
2022-06-17 00:28:44 +02:00
|
|
|
mNavigator->removeAgent(getPathfindingAgentBounds(ptr));
|
2019-03-03 15:14:51 +03:00
|
|
|
|
2022-01-15 13:04:15 +01:00
|
|
|
ptr.getCellRef().setScale(scale);
|
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
mWorldScene->removeFromPagedRefs(ptr);
|
2012-12-04 10:58:43 +01:00
|
|
|
|
2020-11-13 11:39:47 +04:00
|
|
|
if (ptr.getRefData().getBaseNode() != nullptr)
|
2020-05-11 13:37:00 +00:00
|
|
|
mWorldScene->updateObjectScale(ptr);
|
2019-03-03 15:14:51 +03:00
|
|
|
|
|
|
|
if (mPhysics->getActor(ptr))
|
2023-01-17 23:31:17 +01:00
|
|
|
{
|
|
|
|
const DetourNavigator::AgentBounds agentBounds = getPathfindingAgentBounds(ptr);
|
|
|
|
if (!mNavigator->addAgent(agentBounds))
|
|
|
|
Log(Debug::Warning) << "Scaled agent bounds are not supported by navigator: " << agentBounds;
|
|
|
|
}
|
2019-03-03 15:51:02 +03:00
|
|
|
else if (const auto object = mPhysics->getObject(ptr))
|
2021-07-05 00:20:44 +02:00
|
|
|
updateNavigatorObject(*object);
|
2012-05-25 18:23:06 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 19:17:17 +02:00
|
|
|
void World::rotateObject(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)
|
2012-05-29 10:15:29 +02:00
|
|
|
{
|
2014-06-14 17:56:41 +02:00
|
|
|
ESM::Position pos = ptr.getRefData().getPosition();
|
|
|
|
float* objRot = pos.rot;
|
2019-11-30 12:29:31 +01:00
|
|
|
if (flags & MWBase::RotationFlag_adjust)
|
2013-04-28 01:14:58 -07:00
|
|
|
{
|
2015-06-03 21:37:21 +02:00
|
|
|
objRot[0] += rot.x();
|
|
|
|
objRot[1] += rot.y();
|
|
|
|
objRot[2] += rot.z();
|
2013-04-28 01:14:58 -07:00
|
|
|
}
|
|
|
|
else
|
2012-11-05 19:48:07 +00:00
|
|
|
{
|
2015-06-03 21:37:21 +02:00
|
|
|
objRot[0] = rot.x();
|
|
|
|
objRot[1] = rot.y();
|
|
|
|
objRot[2] = rot.z();
|
2013-04-28 01:14:58 -07:00
|
|
|
}
|
2012-09-01 17:28:12 -04:00
|
|
|
|
2014-05-22 20:37:22 +02:00
|
|
|
if (ptr.getClass().isActor())
|
2013-04-28 01:14:58 -07:00
|
|
|
{
|
|
|
|
/* HACK? Actors shouldn't really be rotating around X (or Y), but
|
|
|
|
* currently it's done so for rotating the camera, which needs
|
|
|
|
* clamping.
|
|
|
|
*/
|
2021-11-06 07:30:28 +03:00
|
|
|
objRot[0] = std::clamp<float>(objRot[0], -osg::PI_2, osg::PI_2);
|
2021-09-26 17:28:51 +02:00
|
|
|
objRot[1] = Misc::normalizeAngle(objRot[1]);
|
|
|
|
objRot[2] = Misc::normalizeAngle(objRot[2]);
|
2015-11-12 00:52:36 +01:00
|
|
|
}
|
2013-04-28 01:14:58 -07:00
|
|
|
|
2014-06-14 17:56:41 +02:00
|
|
|
ptr.getRefData().setPosition(pos);
|
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
mWorldScene->removeFromPagedRefs(ptr);
|
|
|
|
|
2020-11-13 11:39:47 +04:00
|
|
|
if (ptr.getRefData().getBaseNode() != nullptr)
|
2019-03-03 16:01:52 +03:00
|
|
|
{
|
2019-11-30 12:29:31 +01:00
|
|
|
const auto order
|
|
|
|
= flags & MWBase::RotationFlag_inverseOrder ? RotationOrder::inverse : RotationOrder::direct;
|
|
|
|
mWorldScene->updateObjectRotation(ptr, order);
|
2019-03-03 16:01:52 +03:00
|
|
|
|
|
|
|
if (const auto object = mPhysics->getObject(ptr))
|
2021-07-05 00:20:44 +02:00
|
|
|
updateNavigatorObject(*object);
|
2019-03-03 16:01:52 +03:00
|
|
|
}
|
2012-05-29 10:15:29 +02:00
|
|
|
}
|
|
|
|
|
2014-07-31 04:24:45 +02:00
|
|
|
void World::adjustPosition(const Ptr& ptr, bool force)
|
2013-04-03 23:55:57 +02:00
|
|
|
{
|
2021-01-28 18:37:47 +04:00
|
|
|
if (ptr.isEmpty())
|
|
|
|
{
|
|
|
|
Log(Debug::Warning) << "Unable to adjust position for empty object";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-10 02:43:49 +01:00
|
|
|
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
2013-04-03 23:55:57 +02:00
|
|
|
|
2015-05-12 19:02:56 +02:00
|
|
|
if (!ptr.getRefData().getBaseNode())
|
2013-04-04 16:51:22 +02:00
|
|
|
{
|
|
|
|
// will be adjusted when Ptr's cell becomes active
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-28 18:37:47 +04:00
|
|
|
if (!ptr.isInCell())
|
|
|
|
{
|
|
|
|
Log(Debug::Warning) << "Unable to adjust position for object '" << ptr.getCellRef().getRefId()
|
|
|
|
<< "' - it has no cell";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-06 13:24:42 +01:00
|
|
|
const float terrainHeight
|
|
|
|
= ptr.getCell()->isExterior() ? getTerrainHeightAt(pos) : -std::numeric_limits<float>::max();
|
2020-12-19 16:54:50 +01:00
|
|
|
pos.z() = std::max(pos.z(), terrainHeight)
|
|
|
|
+ 20; // place slightly above terrain. will snap down to ground with code below
|
2018-05-26 11:35:48 +00:00
|
|
|
|
2020-03-21 10:32:47 +04:00
|
|
|
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
|
|
|
bool swims = ptr.getClass().isActor() && isSwimming(ptr)
|
|
|
|
&& !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
|
|
|
|
if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))
|
2013-04-03 23:55:57 +02:00
|
|
|
{
|
2018-10-08 15:50:54 +03:00
|
|
|
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
|
2020-12-06 13:24:42 +01:00
|
|
|
pos.z() = std::min(pos.z(), traced.z());
|
2013-04-03 23:55:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-11 18:18:10 +02:00
|
|
|
moveObject(ptr, ptr.getCell(), pos);
|
2013-04-03 23:55:57 +02:00
|
|
|
}
|
|
|
|
|
2018-10-24 01:40:57 +03:00
|
|
|
void World::fixPosition()
|
2014-06-28 14:59:33 +02:00
|
|
|
{
|
2018-10-24 01:40:57 +03:00
|
|
|
const MWWorld::Ptr actor = getPlayerPtr();
|
2018-09-13 13:21:38 +04:00
|
|
|
const float distance = 128.f;
|
|
|
|
ESM::Position esmPos = actor.getRefData().getPosition();
|
|
|
|
osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0, 0, -1));
|
|
|
|
osg::Vec3f pos(esmPos.asVec3());
|
2014-06-28 14:59:33 +02:00
|
|
|
|
2018-09-13 13:21:38 +04:00
|
|
|
int direction = 0;
|
|
|
|
int fallbackDirections[4] = { direction, (direction + 3) % 4, (direction + 2) % 4, (direction + 1) % 4 };
|
|
|
|
|
|
|
|
osg::Vec3f targetPos = pos;
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
direction = fallbackDirections[i];
|
|
|
|
if (direction == 0)
|
|
|
|
targetPos = pos + (orientation * osg::Vec3f(0, 1, 0)) * distance;
|
|
|
|
else if (direction == 1)
|
|
|
|
targetPos = pos - (orientation * osg::Vec3f(0, 1, 0)) * distance;
|
|
|
|
else if (direction == 2)
|
|
|
|
targetPos = pos - (orientation * osg::Vec3f(1, 0, 0)) * distance;
|
|
|
|
else if (direction == 3)
|
|
|
|
targetPos = pos + (orientation * osg::Vec3f(1, 0, 0)) * distance;
|
|
|
|
|
|
|
|
// destination is free
|
2022-11-06 20:49:59 +01:00
|
|
|
if (!mPhysics->castRay(pos, targetPos, MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door).mHit)
|
2018-09-13 13:21:38 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later
|
|
|
|
osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits);
|
2017-02-10 02:43:49 +01:00
|
|
|
if (traced != pos)
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
|
|
|
esmPos.pos[0] = traced.x();
|
|
|
|
esmPos.pos[1] = traced.y();
|
|
|
|
esmPos.pos[2] = traced.z();
|
2023-01-21 17:45:23 +01:00
|
|
|
std::string_view cell;
|
2022-08-27 13:07:59 +02:00
|
|
|
if (!actor.getCell()->isExterior())
|
2023-01-28 12:07:47 +01:00
|
|
|
cell = actor.getCell()->getCell()->getNameId();
|
2023-01-19 17:31:45 +01:00
|
|
|
MWWorld::ActionTeleport(cell, esmPos, false).execute(actor);
|
2018-09-13 13:21:38 +04:00
|
|
|
}
|
2014-06-28 14:59:33 +02:00
|
|
|
}
|
|
|
|
|
2021-08-15 19:50:28 +02:00
|
|
|
void World::rotateWorldObject(const Ptr& ptr, const osg::Quat& rotate)
|
2018-05-30 14:43:07 +04:00
|
|
|
{
|
2020-11-13 11:39:47 +04:00
|
|
|
if (ptr.getRefData().getBaseNode() != nullptr)
|
2018-05-30 14:43:07 +04:00
|
|
|
{
|
2020-07-09 22:36:57 +02:00
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
mWorldScene->removeFromPagedRefs(ptr);
|
|
|
|
|
2018-05-30 14:43:07 +04:00
|
|
|
mRendering->rotateObject(ptr, rotate);
|
2021-02-05 14:55:42 +01:00
|
|
|
mPhysics->updateRotation(ptr, rotate);
|
2019-08-15 18:18:53 +02:00
|
|
|
|
|
|
|
if (const auto object = mPhysics->getObject(ptr))
|
2021-07-05 00:20:44 +02:00
|
|
|
updateNavigatorObject(*object);
|
2018-05-30 14:43:07 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-15 19:50:28 +02:00
|
|
|
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, const ESM::Position& pos)
|
2012-09-17 13:36:48 +02:00
|
|
|
{
|
2015-12-19 16:47:55 +01:00
|
|
|
return copyObjectToCell(ptr, cell, pos, ptr.getRefData().getCount(), false);
|
2012-09-17 13:36:48 +02:00
|
|
|
}
|
|
|
|
|
2016-02-29 17:16:33 +01:00
|
|
|
MWWorld::Ptr World::safePlaceObject(const ConstPtr& ptr, const ConstPtr& referenceObject,
|
|
|
|
MWWorld::CellStore* referenceCell, int direction, float distance)
|
|
|
|
{
|
|
|
|
ESM::Position ipos = referenceObject.getRefData().getPosition();
|
|
|
|
osg::Vec3f pos(ipos.asVec3());
|
|
|
|
osg::Quat orientation(ipos.rot[2], osg::Vec3f(0, 0, -1));
|
|
|
|
|
2016-02-29 17:26:52 +01:00
|
|
|
int fallbackDirections[4] = { direction, (direction + 3) % 4, (direction + 2) % 4, (direction + 1) % 4 };
|
|
|
|
|
2016-06-02 21:24:19 +02:00
|
|
|
osg::Vec3f spawnPoint = pos;
|
|
|
|
|
2016-02-29 17:16:33 +01:00
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
2016-02-29 17:26:52 +01:00
|
|
|
direction = fallbackDirections[i];
|
2016-02-29 17:16:33 +01:00
|
|
|
if (direction == 0)
|
|
|
|
spawnPoint = pos + (orientation * osg::Vec3f(0, 1, 0)) * distance;
|
|
|
|
else if (direction == 1)
|
|
|
|
spawnPoint = pos - (orientation * osg::Vec3f(0, 1, 0)) * distance;
|
|
|
|
else if (direction == 2)
|
|
|
|
spawnPoint = pos - (orientation * osg::Vec3f(1, 0, 0)) * distance;
|
|
|
|
else if (direction == 3)
|
|
|
|
spawnPoint = pos + (orientation * osg::Vec3f(1, 0, 0)) * distance;
|
2016-03-04 12:25:22 +01:00
|
|
|
|
|
|
|
if (!ptr.getClass().isActor())
|
|
|
|
break;
|
|
|
|
|
|
|
|
// check if spawn point is safe, fall back to another direction if not
|
2016-02-29 17:16:33 +01:00
|
|
|
spawnPoint.z() += 30; // move up a little to account for slopes, will snap down later
|
|
|
|
|
2022-11-06 21:06:32 +01:00
|
|
|
if (!mPhysics
|
|
|
|
->castRay(spawnPoint, osg::Vec3f(pos.x(), pos.y(), pos.z() + 20),
|
|
|
|
MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door)
|
|
|
|
.mHit)
|
2016-02-29 17:16:33 +01:00
|
|
|
{
|
|
|
|
// safe
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-06-02 21:24:19 +02:00
|
|
|
ipos.pos[0] = spawnPoint.x();
|
|
|
|
ipos.pos[1] = spawnPoint.y();
|
|
|
|
ipos.pos[2] = spawnPoint.z();
|
2016-02-29 17:16:33 +01:00
|
|
|
|
2020-12-06 13:26:43 +01:00
|
|
|
if (referenceObject.getClass().isActor())
|
2016-03-04 12:22:13 +01:00
|
|
|
{
|
|
|
|
ipos.rot[0] = 0;
|
|
|
|
ipos.rot[1] = 0;
|
|
|
|
}
|
2016-02-29 17:16:33 +01:00
|
|
|
|
|
|
|
MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);
|
2018-09-13 13:21:38 +04:00
|
|
|
adjustPosition(placed, true); // snap to ground
|
2016-02-29 17:16:33 +01:00
|
|
|
return placed;
|
|
|
|
}
|
|
|
|
|
2010-09-11 12:21:55 +02:00
|
|
|
void World::indexToPosition(int cellX, int cellY, float& x, float& y, bool centre) const
|
2010-08-22 21:30:48 +02:00
|
|
|
{
|
2018-09-17 14:52:43 +04:00
|
|
|
const int cellSize = Constants::CellSizeInUnits;
|
2010-08-22 21:30:48 +02:00
|
|
|
|
2015-03-08 13:07:29 +13:00
|
|
|
x = static_cast<float>(cellSize * cellX);
|
|
|
|
y = static_cast<float>(cellSize * cellY);
|
2010-09-11 12:21:55 +02:00
|
|
|
|
|
|
|
if (centre)
|
|
|
|
{
|
|
|
|
x += cellSize / 2;
|
|
|
|
y += cellSize / 2;
|
|
|
|
}
|
2010-08-22 21:30:48 +02:00
|
|
|
}
|
|
|
|
|
2015-05-12 03:02:15 +02:00
|
|
|
void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity)
|
2013-08-17 22:34:38 -07:00
|
|
|
{
|
2015-05-12 03:02:15 +02:00
|
|
|
mPhysics->queueObjectMovement(ptr, velocity);
|
2013-08-17 22:34:38 -07:00
|
|
|
}
|
2018-09-22 12:57:50 +04:00
|
|
|
|
|
|
|
void World::updateAnimatedCollisionShape(const Ptr& ptr)
|
|
|
|
{
|
|
|
|
mPhysics->updateAnimatedCollisionShape(ptr);
|
|
|
|
}
|
2013-08-17 22:34:38 -07:00
|
|
|
|
2020-11-20 13:11:53 +01:00
|
|
|
void World::doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
2011-01-29 17:39:34 +01:00
|
|
|
{
|
2013-04-30 20:26:59 +02:00
|
|
|
processDoors(duration);
|
2015-06-01 21:41:13 +02:00
|
|
|
mProjectileManager->update(duration);
|
2021-07-31 23:26:10 +02:00
|
|
|
mPhysics->stepSimulation(duration, mDiscardMovements, frameStart, frameNumber, stats);
|
2020-10-23 20:27:07 +02:00
|
|
|
mProjectileManager->processHits();
|
Process movement queue in one or several background threads
Before movement calculation, the main thread prepare a
vector of ActorFrameData, which contains all data necessary to perform
the simulation, and feed it to the solver. At the same time it fetches
the result from the previous background simulation, which in turn is
used by the game mechanics.
Other functions of the physics system (weapon hit for instance)
interrupt the background simulation, with some exceptions described
below.
The number of threads is controlled by the numeric setting
[Physics]
async num threads
In case 'async num threads' > 1 and Bullet doesn't support multiple threads,
1 async thread will be used. 0 means synchronous solver.
Additional settings (will be silently switched off if async num threads = 0)
[Physics]
defer aabb update
Update AABBs of actors and objects in the background thread(s). It is not an especially
costly operation, but it needs exclusive access to the collision world, which blocks
other operations. Since AABB needs to be updated for collision detection, one can queue
them to defer update before start of the movement solver. Extensive tests on as much
as one installation (mine) show no drawback having that switched on.
[Physics]
lineofsight keep inactive cache
Control for how long (how many frames) the line of sight (LOS) request will be kept updated.
When a request for LOS is made for the first time, the background threads are stopped to
service it. From now on, the LOS will be refreshed preemptively as part of the background
routine until it is not required for lineofsight keep inactive cache frames. This mean
that subsequent request will not interrupt the background computation.
2020-10-15 06:11:44 +02:00
|
|
|
mDiscardMovements = false;
|
2021-07-31 23:26:10 +02:00
|
|
|
mPhysics->moveActors();
|
2018-08-25 23:51:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void World::updateNavigator()
|
|
|
|
{
|
2022-09-05 09:23:14 +02:00
|
|
|
auto navigatorUpdateGuard = mNavigator->makeUpdateGuard();
|
|
|
|
|
2022-02-02 01:09:34 +01:00
|
|
|
mPhysics->forEachAnimatedObject([&](const auto& pair) {
|
|
|
|
const auto [object, changed] = pair;
|
|
|
|
if (changed)
|
2022-09-05 09:23:14 +02:00
|
|
|
updateNavigatorObject(*object, navigatorUpdateGuard.get());
|
2022-02-02 01:09:34 +01:00
|
|
|
});
|
2018-08-25 23:51:54 +03:00
|
|
|
|
|
|
|
for (const auto& door : mDoorStates)
|
|
|
|
if (const auto object = mPhysics->getObject(door.first))
|
2022-09-05 09:23:14 +02:00
|
|
|
updateNavigatorObject(*object, navigatorUpdateGuard.get());
|
2018-08-25 23:51:54 +03:00
|
|
|
|
2022-09-05 09:23:14 +02:00
|
|
|
mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3(), navigatorUpdateGuard.get());
|
2013-04-28 15:36:37 +02:00
|
|
|
}
|
|
|
|
|
2022-09-05 09:23:14 +02:00
|
|
|
void World::updateNavigatorObject(
|
|
|
|
const MWPhysics::Object& object, const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
|
2018-08-25 23:51:54 +03:00
|
|
|
{
|
2022-08-25 00:14:17 +02:00
|
|
|
if (object.getShapeInstance()->mVisualCollisionType != Resource::VisualCollisionType::None)
|
|
|
|
return;
|
2021-11-04 01:57:27 +01:00
|
|
|
const MWWorld::Ptr ptr = object.getPtr();
|
|
|
|
const DetourNavigator::ObjectShapes shapes(object.getShapeInstance(),
|
|
|
|
DetourNavigator::ObjectTransform{ ptr.getRefData().getPosition(), ptr.getCellRef().getScale() });
|
2022-09-05 09:23:14 +02:00
|
|
|
mNavigator->updateObject(
|
|
|
|
DetourNavigator::ObjectId(&object), shapes, object.getTransform(), navigatorUpdateGuard);
|
2018-08-25 23:51:54 +03:00
|
|
|
}
|
|
|
|
|
2020-08-03 22:44:16 +02:00
|
|
|
const MWPhysics::RayCastingInterface* World::getRayCasting() const
|
|
|
|
{
|
|
|
|
return mPhysics.get();
|
|
|
|
}
|
|
|
|
|
2019-08-25 15:20:14 +02:00
|
|
|
bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)
|
2019-08-15 10:46:47 +04:00
|
|
|
{
|
|
|
|
const ESM::Position& objPos = door.getRefData().getPosition();
|
2021-04-11 19:17:17 +02:00
|
|
|
auto oldRot = objPos.asRotationVec3();
|
|
|
|
auto newRot = oldRot;
|
2019-08-15 10:46:47 +04:00
|
|
|
|
|
|
|
float minRot = door.getCellRef().getPosition().rot[2];
|
|
|
|
float maxRot = minRot + osg::DegreesToRadians(90.f);
|
|
|
|
|
2020-11-01 22:30:48 +01:00
|
|
|
float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1);
|
2021-04-11 19:17:17 +02:00
|
|
|
float targetRot = std::clamp(oldRot.z() + diff, minRot, maxRot);
|
|
|
|
newRot.z() = targetRot;
|
|
|
|
rotateObject(door, newRot, MWBase::RotationFlag_none);
|
2019-08-15 10:46:47 +04:00
|
|
|
|
2019-08-25 15:20:14 +02:00
|
|
|
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
|
2019-08-15 10:46:47 +04:00
|
|
|
|
|
|
|
/// \todo should use convexSweepTest here
|
2019-09-05 07:49:53 +00:00
|
|
|
bool collisionWithActor = false;
|
2020-11-01 22:30:48 +01:00
|
|
|
for (auto& [ptr, point, normal] :
|
|
|
|
mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor))
|
2019-08-15 10:46:47 +04:00
|
|
|
{
|
2020-11-01 22:30:48 +01:00
|
|
|
|
2019-08-15 10:46:47 +04:00
|
|
|
if (ptr.getClass().isActor())
|
|
|
|
{
|
2020-11-02 13:52:36 +01:00
|
|
|
auto localPoint = objPos.asVec3() - point;
|
|
|
|
osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * localPoint - localPoint;
|
2020-11-01 22:30:48 +01:00
|
|
|
direction.normalize();
|
2020-11-02 13:52:36 +01:00
|
|
|
mPhysics->reportCollision(Misc::Convert::toBullet(point), Misc::Convert::toBullet(normal));
|
2020-11-01 22:30:48 +01:00
|
|
|
if (direction * normal < 0) // door is turning away from actor
|
|
|
|
continue;
|
|
|
|
|
2019-09-05 07:49:53 +00:00
|
|
|
collisionWithActor = true;
|
2020-11-01 22:30:48 +01:00
|
|
|
|
2019-08-15 10:46:47 +04:00
|
|
|
// Collided with actor, ask actor to try to avoid door
|
|
|
|
if (ptr != getPlayerPtr())
|
|
|
|
{
|
|
|
|
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
2020-05-16 21:52:16 +02:00
|
|
|
if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) // Only add it once
|
2019-08-15 10:46:47 +04:00
|
|
|
seq.stack(MWMechanics::AiAvoidDoor(door), ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we need to undo the rotation
|
|
|
|
reached = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 07:49:53 +00:00
|
|
|
// Cancel door closing sound if collision with actor is detected
|
|
|
|
if (collisionWithActor)
|
|
|
|
{
|
|
|
|
const ESM::Door* ref = door.get<ESM::Door>()->mBase;
|
|
|
|
|
|
|
|
if (state == MWWorld::DoorState::Opening)
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& openSound = ref->mOpenSound;
|
2019-09-05 07:49:53 +00:00
|
|
|
if (!openSound.empty()
|
|
|
|
&& MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, openSound))
|
|
|
|
MWBase::Environment::get().getSoundManager()->stopSound3D(door, openSound);
|
|
|
|
}
|
|
|
|
else if (state == MWWorld::DoorState::Closing)
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& closeSound = ref->mCloseSound;
|
2019-09-05 07:49:53 +00:00
|
|
|
if (!closeSound.empty()
|
|
|
|
&& MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))
|
|
|
|
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
|
|
|
|
}
|
2019-09-29 10:28:02 +02:00
|
|
|
|
2021-04-11 19:17:17 +02:00
|
|
|
rotateObject(door, oldRot, MWBase::RotationFlag_none);
|
2019-09-05 07:49:53 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 10:46:47 +04:00
|
|
|
return reached;
|
|
|
|
}
|
|
|
|
|
2013-04-28 15:36:37 +02:00
|
|
|
void World::processDoors(float duration)
|
|
|
|
{
|
2019-08-25 15:20:14 +02:00
|
|
|
auto it = mDoorStates.begin();
|
2013-04-28 15:59:59 +02:00
|
|
|
while (it != mDoorStates.end())
|
2013-04-28 14:59:15 +02:00
|
|
|
{
|
2014-01-28 19:03:20 +01:00
|
|
|
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
|
2014-05-15 01:58:44 +02:00
|
|
|
{
|
|
|
|
// The door is no longer in an active cell, or it was disabled.
|
|
|
|
// Erase from mDoorStates, since we no longer need to move it.
|
|
|
|
// Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to
|
|
|
|
// mDoorStates.
|
2013-04-28 15:36:37 +02:00
|
|
|
mDoorStates.erase(it++);
|
2014-05-15 01:58:44 +02:00
|
|
|
}
|
2013-04-28 14:59:15 +02:00
|
|
|
else
|
|
|
|
{
|
2019-08-15 10:46:47 +04:00
|
|
|
bool reached = rotateDoor(it->first, it->second, duration);
|
2017-01-19 02:19:44 +01:00
|
|
|
|
2014-05-15 01:58:44 +02:00
|
|
|
if (reached)
|
|
|
|
{
|
|
|
|
// Mark as non-moving
|
2019-08-25 15:20:14 +02:00
|
|
|
it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle);
|
2013-04-28 15:59:59 +02:00
|
|
|
mDoorStates.erase(it++);
|
2014-05-15 01:58:44 +02:00
|
|
|
}
|
2013-04-28 15:59:59 +02:00
|
|
|
else
|
|
|
|
++it;
|
2013-04-28 14:59:15 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-29 17:39:34 +01:00
|
|
|
}
|
2011-03-13 22:33:55 +01:00
|
|
|
|
2018-12-31 18:39:20 +03:00
|
|
|
void World::setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external)
|
2018-09-21 16:34:23 +04:00
|
|
|
{
|
2018-12-31 18:39:20 +03:00
|
|
|
MWPhysics::Actor* physicActor = mPhysics->getActor(ptr);
|
|
|
|
if (physicActor && physicActor->getCollisionMode() != internal)
|
|
|
|
{
|
|
|
|
physicActor->enableCollisionMode(internal);
|
|
|
|
physicActor->enableCollisionBody(external);
|
|
|
|
}
|
2018-09-21 16:34:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool World::isActorCollisionEnabled(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
2018-12-31 18:39:20 +03:00
|
|
|
MWPhysics::Actor* physicActor = mPhysics->getActor(ptr);
|
|
|
|
return physicActor && physicActor->getCollisionMode();
|
2018-09-21 16:34:23 +04:00
|
|
|
}
|
|
|
|
|
2011-04-26 21:38:21 +02:00
|
|
|
bool World::toggleCollisionMode()
|
2011-03-13 22:33:55 +01:00
|
|
|
{
|
2018-10-08 15:50:54 +03:00
|
|
|
if (mPhysics->toggleCollisionMode())
|
|
|
|
{
|
|
|
|
adjustPosition(getPlayerPtr(), true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2011-03-13 22:33:55 +01:00
|
|
|
}
|
2011-03-16 09:09:45 +01:00
|
|
|
|
2015-05-02 22:45:27 +02:00
|
|
|
bool World::toggleRenderMode(MWRender::RenderMode mode)
|
2011-03-16 09:09:45 +01:00
|
|
|
{
|
2015-05-03 00:39:01 +02:00
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case MWRender::Render_CollisionDebug:
|
|
|
|
return mPhysics->toggleDebugRendering();
|
|
|
|
default:
|
|
|
|
return mRendering->toggleRenderMode(mode);
|
|
|
|
}
|
2011-03-16 09:09:45 +01:00
|
|
|
}
|
2011-07-22 15:56:54 +02:00
|
|
|
|
2012-11-08 01:52:34 +04:00
|
|
|
const ESM::Potion* World::createRecord(const ESM::Potion& record)
|
2011-07-22 15:56:54 +02:00
|
|
|
{
|
2012-12-04 10:58:43 +01:00
|
|
|
return mStore.insert(record);
|
2011-07-22 15:56:54 +02:00
|
|
|
}
|
|
|
|
|
2012-11-08 01:52:34 +04:00
|
|
|
const ESM::Class* World::createRecord(const ESM::Class& record)
|
2011-07-22 15:56:54 +02:00
|
|
|
{
|
2012-11-08 01:52:34 +04:00
|
|
|
return mStore.insert(record);
|
2011-07-22 15:56:54 +02:00
|
|
|
}
|
|
|
|
|
2012-11-08 01:52:34 +04:00
|
|
|
const ESM::Spell* World::createRecord(const ESM::Spell& record)
|
2012-10-15 21:54:19 +02:00
|
|
|
{
|
2012-11-08 01:52:34 +04:00
|
|
|
return mStore.insert(record);
|
2011-07-22 15:56:54 +02:00
|
|
|
}
|
2011-09-26 11:11:30 +02:00
|
|
|
|
|
|
|
const ESM::Cell* World::createRecord(const ESM::Cell& record)
|
|
|
|
{
|
2012-11-06 17:51:38 +04:00
|
|
|
return mStore.insert(record);
|
2011-09-26 11:11:30 +02:00
|
|
|
}
|
|
|
|
|
2014-12-17 01:05:32 +01:00
|
|
|
const ESM::CreatureLevList* World::createOverrideRecord(const ESM::CreatureLevList& record)
|
|
|
|
{
|
|
|
|
return mStore.overrideRecord(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::ItemLevList* World::createOverrideRecord(const ESM::ItemLevList& record)
|
|
|
|
{
|
|
|
|
return mStore.overrideRecord(record);
|
|
|
|
}
|
|
|
|
|
2020-06-02 21:59:37 +02:00
|
|
|
const ESM::Creature* World::createOverrideRecord(const ESM::Creature& record)
|
|
|
|
{
|
|
|
|
return mStore.overrideRecord(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::NPC* World::createOverrideRecord(const ESM::NPC& record)
|
|
|
|
{
|
|
|
|
return mStore.overrideRecord(record);
|
|
|
|
}
|
|
|
|
|
2020-10-20 09:22:43 +00:00
|
|
|
const ESM::Container* World::createOverrideRecord(const ESM::Container& record)
|
|
|
|
{
|
|
|
|
return mStore.overrideRecord(record);
|
|
|
|
}
|
|
|
|
|
2012-11-08 16:37:57 +04:00
|
|
|
const ESM::NPC* World::createRecord(const ESM::NPC& record)
|
|
|
|
{
|
2012-11-10 11:51:48 +04:00
|
|
|
bool update = false;
|
2012-12-04 10:58:43 +01:00
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
if (record.mId == "Player")
|
2012-12-04 10:58:43 +01:00
|
|
|
{
|
2012-11-10 11:51:48 +04:00
|
|
|
const ESM::NPC* player = mPlayer->getPlayer().get<ESM::NPC>()->mBase;
|
2011-09-26 11:11:30 +02:00
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
update = record.isMale() != player->isMale() || !(record.mRace == player->mRace)
|
|
|
|
|| !(record.mHead == player->mHead) || !(record.mHair == player->mHair);
|
2011-09-26 11:11:30 +02:00
|
|
|
}
|
2012-11-10 11:51:48 +04:00
|
|
|
const ESM::NPC* ret = mStore.insert(record);
|
|
|
|
if (update)
|
|
|
|
{
|
2015-06-01 00:50:48 +02:00
|
|
|
renderPlayer();
|
2012-11-10 11:51:48 +04:00
|
|
|
}
|
|
|
|
return ret;
|
2011-09-26 11:11:30 +02:00
|
|
|
}
|
2011-11-21 14:08:44 +01:00
|
|
|
|
2013-03-28 17:41:00 +01:00
|
|
|
const ESM::Armor* World::createRecord(const ESM::Armor& record)
|
|
|
|
{
|
|
|
|
return mStore.insert(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::Weapon* World::createRecord(const ESM::Weapon& record)
|
|
|
|
{
|
|
|
|
return mStore.insert(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::Clothing* World::createRecord(const ESM::Clothing& record)
|
|
|
|
{
|
|
|
|
return mStore.insert(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::Enchantment* World::createRecord(const ESM::Enchantment& record)
|
|
|
|
{
|
|
|
|
return mStore.insert(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ESM::Book* World::createRecord(const ESM::Book& record)
|
|
|
|
{
|
|
|
|
return mStore.insert(record);
|
|
|
|
}
|
|
|
|
|
2012-11-03 19:29:55 +00:00
|
|
|
void World::update(float duration, bool paused)
|
2012-01-23 14:33:06 +01:00
|
|
|
{
|
2014-01-11 06:47:58 +01:00
|
|
|
if (mGoToJail && !paused)
|
|
|
|
goToJail();
|
|
|
|
|
2018-08-13 08:30:50 +04:00
|
|
|
// Reset "traveling" flag - there was a frame to detect traveling.
|
|
|
|
mPlayerTraveling = false;
|
|
|
|
|
2018-08-13 11:10:01 +04:00
|
|
|
// The same thing for "in jail" flag: reset it if:
|
|
|
|
// 1. Player was in jail
|
|
|
|
// 2. Jailing window was closed
|
|
|
|
if (mPlayerInJail && !mGoToJail && !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail))
|
|
|
|
mPlayerInJail = false;
|
2014-01-11 06:47:58 +01:00
|
|
|
|
2014-10-09 19:41:51 +13:00
|
|
|
updateWeather(duration, paused);
|
2013-03-03 15:10:40 +01:00
|
|
|
|
2021-09-18 15:52:03 +02:00
|
|
|
updateNavigator();
|
2013-08-17 22:34:38 -07:00
|
|
|
|
2022-05-05 00:57:15 +03:00
|
|
|
mPlayer->update();
|
2017-07-19 13:05:51 +02:00
|
|
|
|
2017-02-08 18:52:46 +01:00
|
|
|
mPhysics->debugDraw();
|
|
|
|
|
2022-05-06 20:12:17 +02:00
|
|
|
mWorldScene->update(duration);
|
|
|
|
|
|
|
|
mRendering->update(duration, paused);
|
2014-05-26 16:43:19 +02:00
|
|
|
|
2014-12-08 23:26:09 +01:00
|
|
|
updateSoundListener();
|
2015-05-26 16:40:44 +02:00
|
|
|
|
2017-02-15 00:55:35 +01:00
|
|
|
mSpellPreloadTimer -= duration;
|
|
|
|
if (mSpellPreloadTimer <= 0.f)
|
|
|
|
{
|
|
|
|
mSpellPreloadTimer = 0.1f;
|
|
|
|
preloadSpells();
|
|
|
|
}
|
2021-09-18 15:52:03 +02:00
|
|
|
|
|
|
|
if (mWorldScene->hasCellLoaded())
|
|
|
|
{
|
2022-09-05 09:23:14 +02:00
|
|
|
mNavigator->wait(DetourNavigator::WaitConditionType::requiredTilesPresent,
|
|
|
|
MWBase::Environment::get().getWindowManager()->getLoadingScreen());
|
2021-09-18 15:52:03 +02:00
|
|
|
mWorldScene->resetCellLoaded();
|
|
|
|
}
|
2019-01-07 14:27:31 +04:00
|
|
|
}
|
|
|
|
|
2020-11-20 13:11:53 +01:00
|
|
|
void World::updatePhysics(
|
|
|
|
float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
2019-01-07 14:27:31 +04:00
|
|
|
{
|
|
|
|
if (!paused)
|
|
|
|
{
|
2020-11-20 13:11:53 +01:00
|
|
|
doPhysics(duration, frameStart, frameNumber, stats);
|
2019-01-07 14:27:31 +04:00
|
|
|
}
|
2021-02-12 19:33:08 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// zero the async stats if we are paused
|
|
|
|
stats.setAttribute(frameNumber, "physicsworker_time_begin", 0);
|
|
|
|
stats.setAttribute(frameNumber, "physicsworker_time_taken", 0);
|
|
|
|
stats.setAttribute(frameNumber, "physicsworker_time_end", 0);
|
|
|
|
}
|
2015-05-26 16:40:44 +02:00
|
|
|
}
|
|
|
|
|
2017-02-15 00:55:35 +01:00
|
|
|
void World::preloadSpells()
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();
|
2017-02-15 00:55:35 +01:00
|
|
|
if (!selectedSpell.empty())
|
|
|
|
{
|
|
|
|
const ESM::Spell* spell = mStore.get<ESM::Spell>().search(selectedSpell);
|
|
|
|
if (spell)
|
|
|
|
preloadEffects(&spell->mEffects);
|
|
|
|
}
|
|
|
|
const MWWorld::Ptr& selectedEnchantItem
|
|
|
|
= MWBase::Environment::get().getWindowManager()->getSelectedEnchantItem();
|
|
|
|
if (!selectedEnchantItem.isEmpty())
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);
|
2017-02-15 00:55:35 +01:00
|
|
|
if (!enchantId.empty())
|
|
|
|
{
|
2020-07-08 14:12:55 +04:00
|
|
|
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
|
2017-02-15 00:55:35 +01:00
|
|
|
if (ench)
|
|
|
|
preloadEffects(&ench->mEffects);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const MWWorld::Ptr& selectedWeapon = MWBase::Environment::get().getWindowManager()->getSelectedWeapon();
|
|
|
|
if (!selectedWeapon.isEmpty())
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& enchantId = selectedWeapon.getClass().getEnchantment(selectedWeapon);
|
2017-02-15 00:55:35 +01:00
|
|
|
if (!enchantId.empty())
|
|
|
|
{
|
|
|
|
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
|
|
|
|
if (ench && ench->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
|
|
preloadEffects(&ench->mEffects);
|
|
|
|
}
|
|
|
|
}
|
2013-01-07 22:48:24 -08:00
|
|
|
}
|
|
|
|
|
2014-12-08 23:26:09 +01:00
|
|
|
void World::updateSoundListener()
|
|
|
|
{
|
2021-06-20 00:57:41 +02:00
|
|
|
osg::Vec3f cameraPosition = mRendering->getCamera()->getPosition();
|
2015-05-12 19:02:56 +02:00
|
|
|
const ESM::Position& refpos = getPlayerPtr().getRefData().getPosition();
|
2015-12-03 15:16:20 +01:00
|
|
|
osg::Vec3f listenerPos;
|
2015-05-12 19:02:56 +02:00
|
|
|
|
2015-12-03 15:16:20 +01:00
|
|
|
if (isFirstPerson())
|
2021-06-20 00:57:41 +02:00
|
|
|
listenerPos = cameraPosition;
|
2015-12-03 15:16:20 +01:00
|
|
|
else
|
|
|
|
listenerPos = refpos.asVec3() + osg::Vec3f(0, 0, 1.85f * mPhysics->getHalfExtents(getPlayerPtr()).z());
|
2015-05-12 19:02:56 +02:00
|
|
|
|
2015-12-03 15:16:20 +01:00
|
|
|
osg::Quat listenerOrient = osg::Quat(refpos.rot[1], osg::Vec3f(0, -1, 0))
|
2015-05-12 19:02:56 +02:00
|
|
|
* osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1));
|
|
|
|
|
2015-12-03 15:16:20 +01:00
|
|
|
osg::Vec3f forward = listenerOrient * osg::Vec3f(0, 1, 0);
|
|
|
|
osg::Vec3f up = listenerOrient * osg::Vec3f(0, 0, 1);
|
|
|
|
|
2021-06-20 00:57:41 +02:00
|
|
|
bool underwater = isUnderwater(getPlayerPtr().getCell(), cameraPosition);
|
2015-05-12 19:02:56 +02:00
|
|
|
|
2015-12-03 15:16:20 +01:00
|
|
|
MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater);
|
2014-12-08 23:26:09 +01:00
|
|
|
}
|
|
|
|
|
2013-01-07 22:48:24 -08:00
|
|
|
void World::updateWindowManager()
|
|
|
|
{
|
2018-01-14 00:26:37 +00:00
|
|
|
try
|
2012-10-18 22:21:39 +02:00
|
|
|
{
|
2018-01-14 00:26:37 +00:00
|
|
|
// inform the GUI about focused object
|
|
|
|
MWWorld::Ptr object = getFacedObject();
|
2012-04-15 17:10:08 +02:00
|
|
|
|
2018-01-14 00:26:37 +00:00
|
|
|
// retrieve object dimensions so we know where to place the floating label
|
|
|
|
if (!object.isEmpty())
|
|
|
|
{
|
2020-05-11 13:37:00 +00:00
|
|
|
osg::BoundingBox bb = mPhysics->getBoundingBox(object);
|
|
|
|
if (!bb.valid() && object.getRefData().getBaseNode())
|
|
|
|
{
|
|
|
|
osg::ComputeBoundsVisitor computeBoundsVisitor;
|
|
|
|
computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem | MWRender::Mask_Effect));
|
|
|
|
object.getRefData().getBaseNode()->accept(computeBoundsVisitor);
|
|
|
|
bb = computeBoundsVisitor.getBoundingBox();
|
|
|
|
}
|
|
|
|
osg::Vec4f screenBounds = mRendering->getScreenBounds(bb);
|
2018-01-14 00:26:37 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(
|
|
|
|
screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());
|
|
|
|
}
|
2017-07-20 20:59:36 +02:00
|
|
|
|
2018-01-14 00:26:37 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->setFocusObject(object);
|
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Error) << "Error updating window manager: " << e.what();
|
2018-01-14 00:26:37 +00:00
|
|
|
}
|
2013-01-07 22:48:24 -08:00
|
|
|
}
|
2012-03-25 20:52:56 +02:00
|
|
|
|
2015-05-24 03:36:34 +02:00
|
|
|
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
|
2013-01-07 22:48:24 -08:00
|
|
|
{
|
2020-07-25 23:33:50 +02:00
|
|
|
const float camDist = mRendering->getCamera()->getCameraDistance();
|
2016-07-10 17:09:21 +02:00
|
|
|
maxDistance += camDist;
|
2016-07-04 02:11:30 +09:00
|
|
|
MWWorld::Ptr facedObject;
|
2016-07-06 22:09:39 +09:00
|
|
|
MWRender::RenderingManager::RayResult rayToObject;
|
2014-01-01 23:30:58 +01:00
|
|
|
|
2013-01-07 22:48:24 -08:00
|
|
|
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
|
|
{
|
|
|
|
float x, y;
|
|
|
|
MWBase::Environment::get().getWindowManager()->getMousePosition(x, y);
|
2016-07-06 22:09:39 +09:00
|
|
|
rayToObject = mRendering->castCameraToViewportRay(x, y, maxDistance, ignorePlayer);
|
2013-01-07 22:48:24 -08:00
|
|
|
}
|
|
|
|
else
|
2016-07-06 22:09:39 +09:00
|
|
|
rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer);
|
|
|
|
|
|
|
|
facedObject = rayToObject.mHitObject;
|
2021-01-22 15:48:37 +01:00
|
|
|
if (facedObject.isEmpty() && rayToObject.mHitRefnum.isSet())
|
2020-05-11 13:37:00 +00:00
|
|
|
{
|
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
|
|
|
{
|
|
|
|
facedObject = cellstore->searchViaRefNum(rayToObject.mHitRefnum);
|
|
|
|
if (!facedObject.isEmpty())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-07-09 02:12:58 +09:00
|
|
|
if (rayToObject.mHit)
|
2016-07-10 17:09:21 +02:00
|
|
|
mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist;
|
2016-07-07 21:46:58 +09:00
|
|
|
else
|
2016-07-09 02:12:58 +09:00
|
|
|
mDistanceToFacedObject = -1;
|
2016-07-06 22:09:39 +09:00
|
|
|
return facedObject;
|
2014-07-26 18:01:53 +02:00
|
|
|
}
|
|
|
|
|
2022-03-18 00:27:16 +01:00
|
|
|
bool World::castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
|
|
|
|
bool ignorePlayer, bool ignoreActors)
|
|
|
|
{
|
|
|
|
MWRender::RenderingManager::RayResult rayRes = mRendering->castRay(from, to, ignorePlayer, ignoreActors);
|
|
|
|
res.mHit = rayRes.mHit;
|
|
|
|
res.mHitPos = rayRes.mHitPointWorld;
|
|
|
|
res.mHitNormal = rayRes.mHitNormalWorld;
|
|
|
|
res.mHitObject = rayRes.mHitObject;
|
|
|
|
if (res.mHitObject.isEmpty() && rayRes.mHitRefnum.isSet())
|
|
|
|
{
|
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
|
|
|
{
|
|
|
|
res.mHitObject = cellstore->searchViaRefNum(rayRes.mHitRefnum);
|
|
|
|
if (!res.mHitObject.isEmpty())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res.mHit;
|
|
|
|
}
|
|
|
|
|
2012-02-23 21:12:06 +01:00
|
|
|
bool World::isCellExterior() const
|
|
|
|
{
|
2014-10-02 16:30:23 +02:00
|
|
|
const CellStore* currentCell = mWorldScene->getCurrentCell();
|
2012-02-23 21:12:06 +01:00
|
|
|
if (currentCell)
|
|
|
|
{
|
2014-02-21 11:35:46 +01:00
|
|
|
return currentCell->getCell()->isExterior();
|
2012-02-23 21:12:06 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-15 16:01:41 +01:00
|
|
|
|
2012-02-23 21:12:06 +01:00
|
|
|
bool World::isCellQuasiExterior() const
|
|
|
|
{
|
2014-10-02 16:30:23 +02:00
|
|
|
const CellStore* currentCell = mWorldScene->getCurrentCell();
|
2012-02-23 21:12:06 +01:00
|
|
|
if (currentCell)
|
|
|
|
{
|
2023-01-22 23:40:55 +01:00
|
|
|
return currentCell->getCell()->isQuasiExterior();
|
2012-02-23 21:12:06 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2012-03-15 16:01:41 +01:00
|
|
|
|
2012-02-25 21:34:38 +01:00
|
|
|
int World::getCurrentWeather() const
|
|
|
|
{
|
2015-04-14 15:55:56 +02:00
|
|
|
return mWeatherManager->getWeatherID();
|
2012-02-25 21:34:38 +01:00
|
|
|
}
|
2012-03-15 16:01:41 +01:00
|
|
|
|
2022-05-13 18:58:00 -07:00
|
|
|
int World::getNextWeather() const
|
|
|
|
{
|
|
|
|
return mWeatherManager->getNextWeatherID();
|
|
|
|
}
|
|
|
|
|
|
|
|
float World::getWeatherTransition() const
|
|
|
|
{
|
|
|
|
return mWeatherManager->getTransitionFactor();
|
|
|
|
}
|
|
|
|
|
2019-01-27 16:55:36 +04:00
|
|
|
unsigned int World::getNightDayMode() const
|
|
|
|
{
|
|
|
|
return mWeatherManager->getNightDayMode();
|
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void World::changeWeather(const ESM::RefId& region, const unsigned int id)
|
2012-02-26 11:51:02 +01:00
|
|
|
{
|
2015-04-14 15:55:56 +02:00
|
|
|
mWeatherManager->changeWeather(region, id);
|
2012-02-26 11:51:02 +01:00
|
|
|
}
|
2012-03-15 16:01:41 +01:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void World::modRegion(const ESM::RefId& regionid, const std::vector<char>& chances)
|
2013-07-27 07:10:18 -07:00
|
|
|
{
|
2015-04-14 15:55:56 +02:00
|
|
|
mWeatherManager->modRegion(regionid, chances);
|
2013-07-27 07:10:18 -07:00
|
|
|
}
|
|
|
|
|
2015-12-06 20:43:50 +01:00
|
|
|
struct GetDoorMarkerVisitor
|
2012-08-28 17:30:34 +02:00
|
|
|
{
|
2015-12-06 20:43:50 +01:00
|
|
|
std::vector<World::DoorMarker>& mOut;
|
|
|
|
|
|
|
|
bool operator()(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
|
|
|
MWWorld::LiveCellRef<ESM::Door>& ref = *static_cast<MWWorld::LiveCellRef<ESM::Door>*>(ptr.getBase());
|
|
|
|
|
|
|
|
if (!ref.mData.isEnabled() || ref.mData.isDeleted())
|
|
|
|
return true;
|
2014-05-11 15:22:46 +02:00
|
|
|
|
2014-05-25 14:13:07 +02:00
|
|
|
if (ref.mRef.getTeleport())
|
2012-08-28 17:30:34 +02:00
|
|
|
{
|
|
|
|
World::DoorMarker newMarker;
|
2012-12-23 23:23:24 +04:00
|
|
|
newMarker.name = MWClass::Door::getDestination(ref);
|
2012-08-28 17:30:34 +02:00
|
|
|
|
2015-01-31 17:50:19 +01:00
|
|
|
ESM::CellId cellid;
|
|
|
|
if (!ref.mRef.getDestCell().empty())
|
|
|
|
{
|
|
|
|
cellid.mWorldspace = ref.mRef.getDestCell();
|
|
|
|
cellid.mPaged = false;
|
2016-01-21 16:08:04 +01:00
|
|
|
cellid.mIndex.mX = 0;
|
|
|
|
cellid.mIndex.mY = 0;
|
2015-01-31 17:50:19 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cellid.mPaged = true;
|
2022-05-23 19:21:44 +02:00
|
|
|
const osg::Vec2i index
|
|
|
|
= positionToCellIndex(ref.mRef.getDoorDest().pos[0], ref.mRef.getDoorDest().pos[1]);
|
|
|
|
cellid.mIndex.mX = index.x();
|
|
|
|
cellid.mIndex.mY = index.y();
|
2015-01-31 17:50:19 +01:00
|
|
|
}
|
|
|
|
newMarker.dest = cellid;
|
|
|
|
|
2012-08-28 17:30:34 +02:00
|
|
|
ESM::Position pos = ref.mData.getPosition();
|
|
|
|
|
|
|
|
newMarker.x = pos.pos[0];
|
|
|
|
newMarker.y = pos.pos[1];
|
2015-12-06 20:43:50 +01:00
|
|
|
mOut.push_back(newMarker);
|
2012-08-28 17:30:34 +02:00
|
|
|
}
|
2015-12-06 20:43:50 +01:00
|
|
|
return true;
|
2012-08-28 17:30:34 +02:00
|
|
|
}
|
2015-12-06 20:43:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
void World::getDoorMarkers(CellStore* cell, std::vector<World::DoorMarker>& out)
|
|
|
|
{
|
2022-05-06 20:36:25 +02:00
|
|
|
GetDoorMarkerVisitor visitor{ out };
|
2015-12-06 20:43:50 +01:00
|
|
|
cell->forEachType<ESM::Door>(visitor);
|
2012-08-28 17:30:34 +02:00
|
|
|
}
|
|
|
|
|
2012-03-29 15:49:24 +02:00
|
|
|
void World::setWaterHeight(const float height)
|
|
|
|
{
|
2015-05-26 16:40:44 +02:00
|
|
|
mPhysics->setWaterHeight(height);
|
2015-06-02 16:35:35 +02:00
|
|
|
mRendering->setWaterHeight(height);
|
2012-03-29 15:49:24 +02:00
|
|
|
}
|
|
|
|
|
2014-05-16 09:21:08 +02:00
|
|
|
bool World::toggleWater()
|
2012-03-29 18:33:08 +02:00
|
|
|
{
|
2015-06-02 16:35:35 +02:00
|
|
|
return mRendering->toggleRenderMode(MWRender::Render_Water);
|
2012-03-29 18:33:08 +02:00
|
|
|
}
|
|
|
|
|
2014-09-30 15:53:27 +02:00
|
|
|
bool World::toggleWorld()
|
|
|
|
{
|
2015-06-02 16:35:35 +02:00
|
|
|
return mRendering->toggleRenderMode(MWRender::Render_Scene);
|
2014-09-30 15:53:27 +02:00
|
|
|
}
|
|
|
|
|
2018-06-13 01:48:31 +02:00
|
|
|
bool World::toggleBorders()
|
|
|
|
{
|
|
|
|
return mRendering->toggleBorders();
|
|
|
|
}
|
|
|
|
|
2013-01-31 18:45:32 +00:00
|
|
|
void World::PCDropped(const Ptr& item)
|
|
|
|
{
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = item.getClass().getScript(item);
|
2013-02-06 10:12:40 +01:00
|
|
|
|
|
|
|
// Set OnPCDrop Variable on item's script, if it has a script with that variable declared
|
2022-08-11 22:51:55 +02:00
|
|
|
if (!script.empty())
|
2013-08-14 17:05:42 -07:00
|
|
|
item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1);
|
2013-01-31 18:45:32 +00:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:42:59 +01:00
|
|
|
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount)
|
2012-05-14 17:41:17 +02:00
|
|
|
{
|
2015-06-01 01:57:15 +02:00
|
|
|
const float maxDist = 200.f;
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2015-06-05 02:26:16 +02:00
|
|
|
MWRender::RenderingManager::RayResult result
|
|
|
|
= mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2014-03-07 06:11:00 +01:00
|
|
|
CellStore* cell = getPlayerPtr().getCell();
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2014-01-08 18:39:44 +01:00
|
|
|
ESM::Position pos = getPlayerPtr().getRefData().getPosition();
|
2015-06-01 01:57:15 +02:00
|
|
|
if (result.mHit)
|
|
|
|
{
|
2015-06-05 02:26:16 +02:00
|
|
|
pos.pos[0] = result.mHitPointWorld.x();
|
|
|
|
pos.pos[1] = result.mHitPointWorld.y();
|
|
|
|
pos.pos[2] = result.mHitPointWorld.z();
|
2015-06-01 01:57:15 +02:00
|
|
|
}
|
2013-03-09 13:27:24 +01:00
|
|
|
// We want only the Z part of the player's rotation
|
|
|
|
pos.rot[0] = 0;
|
|
|
|
pos.rot[1] = 0;
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2013-08-13 01:19:33 +02:00
|
|
|
// copy the object and set its count
|
2015-12-19 16:47:55 +01:00
|
|
|
Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);
|
2013-08-13 01:19:33 +02:00
|
|
|
|
|
|
|
// only the player place items in the world, so no need to check actor
|
2013-01-31 18:45:32 +00:00
|
|
|
PCDropped(dropped);
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2014-05-16 03:19:38 +02:00
|
|
|
return dropped;
|
2012-05-14 17:41:17 +02:00
|
|
|
}
|
|
|
|
|
2012-05-15 16:47:23 +02:00
|
|
|
bool World::canPlaceObject(float cursorX, float cursorY)
|
|
|
|
{
|
2015-06-01 01:57:15 +02:00
|
|
|
const float maxDist = 200.f;
|
2015-06-05 02:26:16 +02:00
|
|
|
MWRender::RenderingManager::RayResult result
|
|
|
|
= mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
|
2015-06-01 01:57:15 +02:00
|
|
|
|
|
|
|
if (result.mHit)
|
2014-05-27 17:39:04 +02:00
|
|
|
{
|
|
|
|
// check if the wanted position is on a flat surface, and not e.g. against a vertical wall
|
2015-06-05 03:41:10 +02:00
|
|
|
if (std::acos((result.mHitNormalWorld / result.mHitNormalWorld.length()) * osg::Vec3f(0, 0, 1))
|
|
|
|
>= osg::DegreesToRadians(30.f))
|
2014-06-06 00:43:07 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2014-05-27 17:39:04 +02:00
|
|
|
}
|
|
|
|
else
|
2012-05-15 16:47:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-19 16:47:55 +01:00
|
|
|
Ptr World::copyObjectToCell(const ConstPtr& object, CellStore* cell, ESM::Position pos, int count, bool adjustPos)
|
2012-07-25 10:47:59 +04:00
|
|
|
{
|
2020-10-18 01:40:58 +03:00
|
|
|
if (!cell)
|
|
|
|
throw std::runtime_error("copyObjectToCell(): cannot copy object to null cell");
|
2014-03-07 06:11:00 +01:00
|
|
|
if (cell->isExterior())
|
2014-01-14 07:40:17 +01:00
|
|
|
{
|
2022-05-23 19:21:44 +02:00
|
|
|
const osg::Vec2i index = positionToCellIndex(pos.pos[0], pos.pos[1]);
|
2022-12-15 20:56:17 +01:00
|
|
|
cell = mWorldModel.getExterior(index.x(), index.y());
|
2014-01-14 07:40:17 +01:00
|
|
|
}
|
|
|
|
|
2015-12-19 16:47:55 +01:00
|
|
|
MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count);
|
2014-01-14 07:40:17 +01:00
|
|
|
|
2014-06-13 23:40:49 +02:00
|
|
|
// Reset some position values that could be uninitialized if this item came from a container
|
|
|
|
dropped.getCellRef().setPosition(pos);
|
2014-07-29 15:55:58 +02:00
|
|
|
dropped.getCellRef().unsetRefNum();
|
2014-06-13 23:40:49 +02:00
|
|
|
|
2014-03-07 06:11:00 +01:00
|
|
|
if (mWorldScene->isCellActive(*cell))
|
|
|
|
{
|
2012-07-26 19:06:48 +04:00
|
|
|
if (dropped.getRefData().isEnabled())
|
|
|
|
{
|
|
|
|
mWorldScene->addObjectToScene(dropped);
|
|
|
|
}
|
2022-11-13 12:04:57 +01:00
|
|
|
const auto& script = dropped.getClass().getScript(dropped);
|
2012-07-26 16:14:11 +04:00
|
|
|
if (!script.empty())
|
|
|
|
{
|
|
|
|
mLocalScripts.add(script, dropped);
|
|
|
|
}
|
2014-03-07 06:11:00 +01:00
|
|
|
addContainerScripts(dropped, cell);
|
2012-07-25 10:47:59 +04:00
|
|
|
}
|
2013-01-31 18:45:32 +00:00
|
|
|
|
2015-05-22 01:43:45 +02:00
|
|
|
if (!object.getClass().isActor() && adjustPos && dropped.getRefData().getBaseNode())
|
|
|
|
{
|
|
|
|
// Adjust position so the location we wanted ends up in the middle of the object bounding box
|
|
|
|
osg::ComputeBoundsVisitor computeBounds;
|
2020-04-20 18:47:14 +02:00
|
|
|
computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem);
|
2015-05-22 01:43:45 +02:00
|
|
|
dropped.getRefData().getBaseNode()->accept(computeBounds);
|
|
|
|
osg::BoundingBox bounds = computeBounds.getBoundingBox();
|
|
|
|
if (bounds.valid())
|
|
|
|
{
|
|
|
|
bounds.set(bounds._min - pos.asVec3(), bounds._max - pos.asVec3());
|
|
|
|
|
|
|
|
osg::Vec3f adjust(
|
|
|
|
(bounds.xMin() + bounds.xMax()) / 2, (bounds.yMin() + bounds.yMax()) / 2, bounds.zMin());
|
|
|
|
pos.pos[0] -= adjust.x();
|
|
|
|
pos.pos[1] -= adjust.y();
|
|
|
|
pos.pos[2] -= adjust.z();
|
2021-04-11 18:18:10 +02:00
|
|
|
moveObject(dropped, pos.asVec3());
|
2015-05-22 01:43:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-31 18:45:32 +00:00
|
|
|
return dropped;
|
2012-07-25 10:47:59 +04:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:42:59 +01:00
|
|
|
MWWorld::Ptr World::dropObjectOnGround(const Ptr& actor, const ConstPtr& object, int amount)
|
2012-05-15 16:47:23 +02:00
|
|
|
{
|
2013-12-05 13:21:26 +01:00
|
|
|
MWWorld::CellStore* cell = actor.getCell();
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2013-01-09 21:16:45 +00:00
|
|
|
ESM::Position pos = actor.getRefData().getPosition();
|
2013-03-09 13:27:24 +01:00
|
|
|
// We want only the Z part of the actor's rotation
|
|
|
|
pos.rot[0] = 0;
|
|
|
|
pos.rot[1] = 0;
|
2012-05-15 16:47:23 +02:00
|
|
|
|
2015-05-22 01:43:45 +02:00
|
|
|
osg::Vec3f orig = pos.asVec3();
|
|
|
|
orig.z() += 20;
|
|
|
|
osg::Vec3f dir(0, 0, -1);
|
2012-08-04 09:34:49 +02:00
|
|
|
|
2015-11-12 23:05:44 +01:00
|
|
|
float len = 1000000.0;
|
2012-07-25 20:25:53 +04:00
|
|
|
|
2015-06-05 02:26:16 +02:00
|
|
|
MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig + dir * len, true, true);
|
2015-06-01 01:57:15 +02:00
|
|
|
if (result.mHit)
|
2015-06-05 02:26:16 +02:00
|
|
|
pos.pos[2] = result.mHitPointWorld.z();
|
2012-07-25 20:25:53 +04:00
|
|
|
|
2013-08-13 01:19:33 +02:00
|
|
|
// copy the object and set its count
|
2015-12-19 16:47:55 +01:00
|
|
|
Ptr dropped = copyObjectToCell(object, cell, pos, amount, true);
|
2013-08-13 01:19:33 +02:00
|
|
|
|
2013-01-31 18:45:32 +00:00
|
|
|
if (actor == mPlayer->getPlayer()) // Only call if dropped by player
|
|
|
|
PCDropped(dropped);
|
2014-05-16 03:19:38 +02:00
|
|
|
return dropped;
|
2012-05-15 16:47:23 +02:00
|
|
|
}
|
2012-05-23 01:32:36 +02:00
|
|
|
|
|
|
|
void World::processChangedSettings(const Settings::CategorySettingVector& settings)
|
|
|
|
{
|
2015-05-14 18:46:04 +02:00
|
|
|
mRendering->processChangedSettings(settings);
|
2012-05-23 01:32:36 +02:00
|
|
|
}
|
2012-06-22 12:56:04 +02:00
|
|
|
|
2015-01-31 20:54:08 +01:00
|
|
|
bool World::isFlying(const MWWorld::Ptr& ptr) const
|
2013-02-18 22:39:43 -08:00
|
|
|
{
|
2013-08-21 07:24:02 -07:00
|
|
|
if (!ptr.getClass().isActor())
|
|
|
|
return false;
|
|
|
|
|
2018-12-05 23:28:26 +03:00
|
|
|
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
|
|
|
|
2015-08-20 18:12:37 +12:00
|
|
|
if (stats.isDead())
|
2014-01-05 15:38:12 +01:00
|
|
|
return false;
|
|
|
|
|
2020-12-22 06:13:35 +03:00
|
|
|
const bool isPlayer = ptr == getPlayerConstPtr();
|
2021-05-28 16:55:54 +02:00
|
|
|
if (!(isPlayer && mGodMode) && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0)
|
2020-12-22 06:13:35 +03:00
|
|
|
return false;
|
|
|
|
|
2014-03-16 08:09:14 +11:00
|
|
|
if (ptr.getClass().canFly(ptr))
|
2020-12-22 06:13:35 +03:00
|
|
|
return true;
|
2014-01-05 15:38:12 +01:00
|
|
|
|
2014-08-16 22:38:22 +02:00
|
|
|
if (stats.getMagicEffects().get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && isLevitationEnabled())
|
2013-08-21 07:24:02 -07:00
|
|
|
return true;
|
|
|
|
|
2015-05-12 19:02:56 +02:00
|
|
|
const MWPhysics::Actor* actor = mPhysics->getActor(ptr);
|
2018-12-05 23:28:26 +03:00
|
|
|
if (!actor)
|
2015-05-12 19:02:56 +02:00
|
|
|
return true;
|
2013-08-21 07:24:02 -07:00
|
|
|
|
2013-02-20 01:55:12 -08:00
|
|
|
return false;
|
2013-02-18 22:39:43 -08:00
|
|
|
}
|
|
|
|
|
2015-01-31 20:54:08 +01:00
|
|
|
bool World::isSlowFalling(const MWWorld::Ptr& ptr) const
|
2013-10-02 22:53:29 +02:00
|
|
|
{
|
|
|
|
if (!ptr.getClass().isActor())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
2014-08-16 22:38:22 +02:00
|
|
|
if (stats.getMagicEffects().get(ESM::MagicEffect::SlowFall).getMagnitude() > 0)
|
2013-10-02 22:53:29 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::isSubmerged(const MWWorld::ConstPtr& object) const
|
2013-08-07 15:34:11 +02:00
|
|
|
{
|
2015-03-08 13:07:29 +13:00
|
|
|
return isUnderwater(object, 1.0f / mSwimHeightScale);
|
2013-08-07 15:34:11 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::isSwimming(const MWWorld::ConstPtr& object) const
|
2012-08-03 14:42:09 +04:00
|
|
|
{
|
2015-01-31 20:54:08 +01:00
|
|
|
return isUnderwater(object, mSwimHeightScale);
|
2015-01-09 21:40:53 +13:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::isWading(const MWWorld::ConstPtr& object) const
|
2015-01-09 21:40:53 +13:00
|
|
|
{
|
2015-01-31 20:54:08 +01:00
|
|
|
const float kneeDeep = 0.25f;
|
2015-01-09 22:17:53 +13:00
|
|
|
return isUnderwater(object, kneeDeep);
|
2015-01-09 21:40:53 +13:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::isUnderwater(const MWWorld::ConstPtr& object, const float heightRatio) const
|
2015-01-09 21:40:53 +13:00
|
|
|
{
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Vec3f pos(object.getRefData().getPosition().asVec3());
|
2012-08-03 14:42:09 +04:00
|
|
|
|
2015-11-01 21:45:58 +01:00
|
|
|
pos.z() += heightRatio * 2 * mPhysics->getRenderingHalfExtents(object).z();
|
2012-08-03 14:42:09 +04:00
|
|
|
|
2018-10-09 10:21:12 +04:00
|
|
|
const CellStore* currCell = object.isInCell()
|
|
|
|
? object.getCell()
|
|
|
|
: nullptr; // currCell == nullptr should only happen for player, during initial startup
|
2017-09-29 22:28:09 +04:00
|
|
|
|
|
|
|
return isUnderwater(currCell, pos);
|
2012-08-03 14:42:09 +04:00
|
|
|
}
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f& pos) const
|
2012-08-03 14:42:09 +04:00
|
|
|
{
|
2017-09-24 16:28:05 +04:00
|
|
|
if (!cell)
|
|
|
|
return false;
|
|
|
|
|
2016-03-02 17:02:30 +01:00
|
|
|
if (!(cell->getCell()->hasWater()))
|
|
|
|
{
|
2012-08-03 14:42:09 +04:00
|
|
|
return false;
|
|
|
|
}
|
2015-06-01 21:41:13 +02:00
|
|
|
return pos.z() < cell->getWaterLevel();
|
2012-08-03 14:42:09 +04:00
|
|
|
}
|
2012-08-14 20:33:29 +04:00
|
|
|
|
2016-08-31 01:05:54 +09:00
|
|
|
bool World::isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr& target) const
|
|
|
|
{
|
|
|
|
const MWWorld::CellStore* cell = target.getCell();
|
|
|
|
if (!cell->getCell()->hasWater())
|
|
|
|
return true;
|
|
|
|
|
2016-11-15 00:20:17 +09:00
|
|
|
float waterlevel = cell->getWaterLevel();
|
|
|
|
|
2018-05-26 17:44:25 +03:00
|
|
|
// SwimHeightScale affects the upper z position an actor can swim to
|
2016-11-15 00:20:17 +09:00
|
|
|
// while in water. Based on observation from the original engine,
|
|
|
|
// the upper z position you get with a +1 SwimHeightScale is the depth
|
|
|
|
// limit for being able to cast water walking on an underwater target.
|
|
|
|
if (isUnderwater(target, mSwimHeightScale + 1)
|
|
|
|
|| (isUnderwater(cell, target.getRefData().getPosition().asVec3())
|
|
|
|
&& !mPhysics->canMoveToWaterSurface(target, waterlevel)))
|
|
|
|
return false; // not castable if too deep or if not enough room to move actor to surface
|
|
|
|
else
|
|
|
|
return true;
|
2016-08-31 01:05:54 +09:00
|
|
|
}
|
|
|
|
|
2013-02-14 22:35:15 -08:00
|
|
|
bool World::isOnGround(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
2015-06-01 02:40:42 +02:00
|
|
|
return mPhysics->isOnGround(ptr);
|
2013-02-14 22:35:15 -08:00
|
|
|
}
|
|
|
|
|
2019-09-19 21:48:43 +04:00
|
|
|
void World::togglePOV(bool force)
|
2015-05-21 23:54:39 +02:00
|
|
|
{
|
2020-07-25 23:33:50 +02:00
|
|
|
mRendering->getCamera()->toggleViewMode(force);
|
2015-05-21 23:54:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool World::isFirstPerson() const
|
|
|
|
{
|
2021-06-20 00:57:41 +02:00
|
|
|
return mRendering->getCamera()->getMode() == MWRender::Camera::Mode::FirstPerson;
|
2015-05-21 23:54:39 +02:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2020-08-04 06:04:59 +00:00
|
|
|
bool World::isPreviewModeEnabled() const
|
|
|
|
{
|
|
|
|
return mRendering->getCamera()->getMode() == MWRender::Camera::Mode::Preview;
|
|
|
|
}
|
2015-05-21 23:54:39 +02:00
|
|
|
|
|
|
|
bool World::toggleVanityMode(bool enable)
|
|
|
|
{
|
2020-07-25 23:33:50 +02:00
|
|
|
return mRendering->getCamera()->toggleVanityMode(enable);
|
2015-05-21 23:54:39 +02:00
|
|
|
}
|
|
|
|
|
2020-06-27 00:58:33 +02:00
|
|
|
void World::disableDeferredPreviewRotation()
|
|
|
|
{
|
2020-07-18 11:48:46 +02:00
|
|
|
mRendering->getCamera()->disableDeferredPreviewRotation();
|
2020-06-27 00:58:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void World::applyDeferredPreviewRotationToPlayer(float dt)
|
|
|
|
{
|
2020-07-18 11:48:46 +02:00
|
|
|
mRendering->getCamera()->applyDeferredPreviewRotationToPlayer(dt);
|
2020-06-27 00:58:33 +02:00
|
|
|
}
|
|
|
|
|
2021-06-14 19:58:04 +02:00
|
|
|
MWRender::Camera* World::getCamera()
|
|
|
|
{
|
|
|
|
return mRendering->getCamera();
|
|
|
|
}
|
2015-05-21 23:54:39 +02:00
|
|
|
|
2013-04-09 19:24:41 +01:00
|
|
|
bool World::vanityRotateCamera(float* rot)
|
|
|
|
{
|
2021-06-20 00:57:41 +02:00
|
|
|
auto* camera = mRendering->getCamera();
|
|
|
|
if (!camera->isVanityOrPreviewModeEnabled())
|
2020-07-25 23:33:50 +02:00
|
|
|
return false;
|
|
|
|
|
2021-06-20 00:57:41 +02:00
|
|
|
camera->setPitch(camera->getPitch() + rot[0]);
|
|
|
|
camera->setYaw(camera->getYaw() + rot[2]);
|
2020-07-25 23:33:50 +02:00
|
|
|
return true;
|
2013-04-09 19:24:41 +01:00
|
|
|
}
|
|
|
|
|
2021-03-23 21:07:57 +01:00
|
|
|
void World::saveLoaded()
|
|
|
|
{
|
|
|
|
mStore.validateDynamic();
|
|
|
|
}
|
|
|
|
|
2013-04-29 17:56:16 +02:00
|
|
|
void World::setupPlayer()
|
2013-04-15 02:56:23 +02:00
|
|
|
{
|
2022-10-18 09:26:55 +02:00
|
|
|
const ESM::NPC* player = mStore.get<ESM::NPC>().find(ESM::RefId::stringRefId("Player"));
|
2013-05-15 17:54:18 +02:00
|
|
|
if (!mPlayer)
|
2022-05-29 13:24:48 +02:00
|
|
|
mPlayer = std::make_unique<MWWorld::Player>(player);
|
2013-05-15 17:54:18 +02:00
|
|
|
else
|
2014-06-05 17:01:22 +02:00
|
|
|
{
|
|
|
|
// Remove the old CharacterController
|
2021-08-28 14:36:30 +02:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
|
2022-06-17 00:28:44 +02:00
|
|
|
mNavigator->removeAgent(getPathfindingAgentBounds(getPlayerConstPtr()));
|
2015-05-28 02:45:38 +02:00
|
|
|
mPhysics->remove(getPlayerPtr());
|
2015-06-16 20:36:48 +02:00
|
|
|
mRendering->removePlayer(getPlayerPtr());
|
2020-12-19 00:02:31 +01:00
|
|
|
MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(getPlayerPtr());
|
2014-06-05 17:01:22 +02:00
|
|
|
|
2013-05-15 17:54:18 +02:00
|
|
|
mPlayer->set(player);
|
2014-06-05 17:01:22 +02:00
|
|
|
}
|
2013-04-28 23:44:44 -07:00
|
|
|
|
2015-05-01 18:21:50 +02:00
|
|
|
Ptr ptr = mPlayer->getPlayer();
|
|
|
|
mRendering->setupPlayer(ptr);
|
2020-12-19 00:02:31 +01:00
|
|
|
MWBase::Environment::get().getLuaManager()->setupPlayer(ptr);
|
2013-04-15 02:56:23 +02:00
|
|
|
}
|
|
|
|
|
2012-08-14 20:33:29 +04:00
|
|
|
void World::renderPlayer()
|
|
|
|
{
|
2021-08-28 14:36:30 +02:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
|
2015-06-01 00:50:48 +02:00
|
|
|
|
2017-02-22 14:54:40 +01:00
|
|
|
MWWorld::Ptr player = getPlayerPtr();
|
|
|
|
|
|
|
|
mRendering->renderPlayer(player);
|
|
|
|
MWRender::NpcAnimation* anim = static_cast<MWRender::NpcAnimation*>(mRendering->getAnimation(player));
|
2023-01-16 23:51:04 +01:00
|
|
|
player.getClass().getInventoryStore(player).setInvListener(anim);
|
2017-02-22 14:54:40 +01:00
|
|
|
player.getClass().getInventoryStore(player).setContListener(anim);
|
2015-05-01 18:21:50 +02:00
|
|
|
|
2022-02-15 17:25:07 +01:00
|
|
|
scaleObject(player, player.getCellRef().getScale(), true); // apply race height
|
2021-04-11 19:17:17 +02:00
|
|
|
rotateObject(player, osg::Vec3f(), MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust);
|
2017-02-08 04:42:15 +01:00
|
|
|
|
2015-05-01 18:21:50 +02:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->add(getPlayerPtr());
|
2020-06-05 18:22:53 +04:00
|
|
|
MWBase::Environment::get().getWindowManager()->watchActor(getPlayerPtr());
|
2014-01-25 16:13:45 +01:00
|
|
|
|
2015-05-28 02:45:38 +02:00
|
|
|
mPhysics->remove(getPlayerPtr());
|
2023-01-14 04:36:06 +03:00
|
|
|
mPhysics->addActor(getPlayerPtr(), getPlayerPtr().getClass().getModel(getPlayerPtr()));
|
2018-03-17 13:41:13 +04:00
|
|
|
|
|
|
|
applyLoopingParticles(player);
|
2018-04-22 19:22:00 +03:00
|
|
|
|
2023-01-17 23:31:17 +01:00
|
|
|
const DetourNavigator::AgentBounds agentBounds = getPathfindingAgentBounds(getPlayerConstPtr());
|
|
|
|
if (!mNavigator->addAgent(agentBounds))
|
|
|
|
Log(Debug::Warning) << "Player agent bounds are not supported by navigator: " << agentBounds;
|
2012-08-14 20:33:29 +04:00
|
|
|
}
|
2012-09-13 00:54:32 +02:00
|
|
|
|
2018-09-23 06:25:20 +03:00
|
|
|
World::RestPermitted World::canRest() const
|
2012-09-18 20:53:32 +02:00
|
|
|
{
|
2013-12-05 13:21:26 +01:00
|
|
|
CellStore* currentCell = mWorldScene->getCurrentCell();
|
2012-09-19 02:53:06 +02:00
|
|
|
|
2013-08-08 18:30:47 -07:00
|
|
|
Ptr player = mPlayer->getPlayer();
|
2015-05-12 19:02:56 +02:00
|
|
|
RefData& refdata = player.getRefData();
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Vec3f playerPos(refdata.getPosition().asVec3());
|
2012-09-19 02:53:06 +02:00
|
|
|
|
2015-05-12 19:02:56 +02:00
|
|
|
const MWPhysics::Actor* actor = mPhysics->getActor(player);
|
|
|
|
if (!actor)
|
2015-01-15 16:05:25 +01:00
|
|
|
throw std::runtime_error("can't find player");
|
2015-01-15 02:03:27 +01:00
|
|
|
|
2018-09-23 02:46:29 +03:00
|
|
|
if (mPlayer->enemiesNearby())
|
|
|
|
return Rest_EnemiesAreNearby;
|
2018-09-23 01:52:56 +03:00
|
|
|
|
2019-04-04 17:18:49 +03:00
|
|
|
if (isUnderwater(currentCell, playerPos) || isWalkingOnWater(player))
|
2018-09-23 01:52:56 +03:00
|
|
|
return Rest_PlayerIsUnderwater;
|
2015-05-12 19:02:56 +02:00
|
|
|
|
2019-12-12 15:20:23 +03:00
|
|
|
float fallHeight = player.getClass().getCreatureStats(player).getFallHeight();
|
|
|
|
float epsilon = 1e-4;
|
|
|
|
if ((actor->getCollisionMode() && (!mPhysics->isOnSolidGround(player) || fallHeight >= epsilon))
|
|
|
|
|| isFlying(player))
|
2019-04-04 17:18:49 +03:00
|
|
|
return Rest_PlayerIsInAir;
|
|
|
|
|
2023-01-22 23:40:55 +01:00
|
|
|
if (currentCell->getCell()->noSleep() || player.getClass().getNpcStats(player).isWerewolf())
|
2018-09-23 01:52:56 +03:00
|
|
|
return Rest_OnlyWaiting;
|
2012-09-19 02:53:06 +02:00
|
|
|
|
2018-09-23 01:52:56 +03:00
|
|
|
return Rest_Allowed;
|
2012-09-18 20:53:32 +02:00
|
|
|
}
|
2012-09-25 02:35:50 +02:00
|
|
|
|
2013-01-16 09:59:19 -08:00
|
|
|
MWRender::Animation* World::getAnimation(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
2021-06-23 18:08:49 +02:00
|
|
|
auto* animation = mRendering->getAnimation(ptr);
|
|
|
|
if (!animation)
|
|
|
|
{
|
|
|
|
mWorldScene->removeFromPagedRefs(ptr);
|
|
|
|
animation = mRendering->getAnimation(ptr);
|
|
|
|
if (animation)
|
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
}
|
|
|
|
return animation;
|
2013-01-16 09:59:19 -08:00
|
|
|
}
|
|
|
|
|
2015-12-19 16:02:47 +01:00
|
|
|
const MWRender::Animation* World::getAnimation(const MWWorld::ConstPtr& ptr) const
|
|
|
|
{
|
|
|
|
return mRendering->getAnimation(ptr);
|
|
|
|
}
|
|
|
|
|
2015-06-03 16:40:16 +02:00
|
|
|
void World::screenshot(osg::Image* image, int w, int h)
|
2014-01-24 17:49:16 +01:00
|
|
|
{
|
2021-01-08 19:33:51 +04:00
|
|
|
mRendering->screenshot(image, w, h);
|
2014-01-24 17:49:16 +01:00
|
|
|
}
|
2018-05-26 17:44:25 +03:00
|
|
|
|
2021-01-08 19:33:51 +04:00
|
|
|
bool World::screenshot360(osg::Image* image)
|
2017-11-07 13:07:11 +01:00
|
|
|
{
|
2021-01-08 19:33:51 +04:00
|
|
|
return mRendering->screenshot360(image);
|
2017-11-07 13:07:11 +01:00
|
|
|
}
|
2014-01-24 17:49:16 +01:00
|
|
|
|
2013-04-28 14:59:15 +02:00
|
|
|
void World::activateDoor(const MWWorld::Ptr& door)
|
|
|
|
{
|
2019-08-25 15:20:14 +02:00
|
|
|
auto state = door.getClass().getDoorState(door);
|
2014-05-15 01:58:44 +02:00
|
|
|
switch (state)
|
2013-04-28 14:59:15 +02:00
|
|
|
{
|
2019-08-25 15:20:14 +02:00
|
|
|
case MWWorld::DoorState::Idle:
|
2015-11-12 00:58:29 +01:00
|
|
|
if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2])
|
2019-08-25 15:20:14 +02:00
|
|
|
state = MWWorld::DoorState::Opening; // if closed, then open
|
2013-04-28 14:59:15 +02:00
|
|
|
else
|
2019-08-25 15:20:14 +02:00
|
|
|
state = MWWorld::DoorState::Closing; // if open, then close
|
2014-05-15 01:58:44 +02:00
|
|
|
break;
|
2019-08-25 15:20:14 +02:00
|
|
|
case MWWorld::DoorState::Closing:
|
|
|
|
state = MWWorld::DoorState::Opening; // if closing, then open
|
2014-05-15 01:58:44 +02:00
|
|
|
break;
|
2019-08-25 15:20:14 +02:00
|
|
|
case MWWorld::DoorState::Opening:
|
2014-05-15 01:58:44 +02:00
|
|
|
default:
|
2019-08-25 15:20:14 +02:00
|
|
|
state = MWWorld::DoorState::Closing; // if opening, then close
|
2014-05-15 01:58:44 +02:00
|
|
|
break;
|
2013-04-28 14:59:15 +02:00
|
|
|
}
|
2014-05-15 01:58:44 +02:00
|
|
|
door.getClass().setDoorState(door, state);
|
|
|
|
mDoorStates[door] = state;
|
2013-04-28 14:59:15 +02:00
|
|
|
}
|
2013-04-29 11:00:15 +02:00
|
|
|
|
2019-08-25 15:20:14 +02:00
|
|
|
void World::activateDoor(const Ptr& door, MWWorld::DoorState state)
|
2014-05-13 03:58:32 -04:00
|
|
|
{
|
2014-05-15 01:58:44 +02:00
|
|
|
door.getClass().setDoorState(door, state);
|
|
|
|
mDoorStates[door] = state;
|
2019-08-25 15:20:14 +02:00
|
|
|
if (state == MWWorld::DoorState::Idle)
|
2019-08-15 10:46:47 +04:00
|
|
|
{
|
2014-07-22 17:55:54 +02:00
|
|
|
mDoorStates.erase(door);
|
2019-08-15 10:46:47 +04:00
|
|
|
rotateDoor(door, state, 1);
|
|
|
|
}
|
2014-05-13 03:58:32 -04:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::getPlayerStandingOn(const MWWorld::ConstPtr& object)
|
2013-05-01 11:15:43 +02:00
|
|
|
{
|
2015-05-30 01:32:00 +02:00
|
|
|
MWWorld::Ptr player = getPlayerPtr();
|
|
|
|
return mPhysics->isActorStandingOn(player, object);
|
2013-05-01 11:15:43 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::getActorStandingOn(const MWWorld::ConstPtr& object)
|
2013-05-01 11:15:43 +02:00
|
|
|
{
|
2015-05-30 01:32:00 +02:00
|
|
|
std::vector<MWWorld::Ptr> actors;
|
|
|
|
mPhysics->getActorsStandingOn(object, actors);
|
2014-07-29 19:01:40 +02:00
|
|
|
return !actors.empty();
|
|
|
|
}
|
|
|
|
|
2018-08-25 10:34:33 +04:00
|
|
|
void World::getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr>& actors)
|
|
|
|
{
|
|
|
|
mPhysics->getActorsStandingOn(object, actors);
|
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::getPlayerCollidingWith(const MWWorld::ConstPtr& object)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
2015-05-30 01:32:00 +02:00
|
|
|
MWWorld::Ptr player = getPlayerPtr();
|
|
|
|
return mPhysics->isActorCollidingWith(player, object);
|
2014-07-29 19:01:40 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
bool World::getActorCollidingWith(const MWWorld::ConstPtr& object)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
2015-05-30 01:32:00 +02:00
|
|
|
std::vector<MWWorld::Ptr> actors;
|
|
|
|
mPhysics->getActorsCollidingWith(object, actors);
|
2014-07-29 19:01:40 +02:00
|
|
|
return !actors.empty();
|
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
void World::hurtStandingActors(const ConstPtr& object, float healthPerSecond)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
2014-10-12 00:13:24 +02:00
|
|
|
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
|
|
return;
|
|
|
|
|
2015-05-30 01:32:00 +02:00
|
|
|
std::vector<MWWorld::Ptr> actors;
|
2015-05-12 04:04:54 +02:00
|
|
|
mPhysics->getActorsStandingOn(object, actors);
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const Ptr& actor : actors)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
2014-10-11 22:21:48 +02:00
|
|
|
if (stats.isDead())
|
|
|
|
continue;
|
2017-03-25 22:40:11 +04:00
|
|
|
|
|
|
|
mPhysics->markAsNonSolid(object);
|
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
if (actor == getPlayerPtr() && mGodMode)
|
2018-12-30 01:27:16 +03:00
|
|
|
continue;
|
2017-03-25 22:40:11 +04:00
|
|
|
|
2014-07-29 19:01:40 +02:00
|
|
|
MWMechanics::DynamicStat<float> health = stats.getHealth();
|
|
|
|
health.setCurrent(health.getCurrent() - healthPerSecond * MWBase::Environment::get().getFrameDuration());
|
|
|
|
stats.setHealth(health);
|
2014-10-11 22:21:48 +02:00
|
|
|
|
|
|
|
if (healthPerSecond > 0.0f)
|
|
|
|
{
|
2015-03-11 23:07:39 +01:00
|
|
|
if (actor == getPlayerPtr())
|
2014-10-11 22:21:48 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
|
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
auto healthDamage = ESM::RefId::stringRefId("Health Damage");
|
|
|
|
if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, healthDamage))
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWBase::Environment::get().getSoundManager()->playSound3D(actor, healthDamage, 1.0f, 1.0f);
|
2014-10-11 22:21:48 +02:00
|
|
|
}
|
2014-07-29 19:01:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-18 17:56:48 +01:00
|
|
|
void World::hurtCollidingActors(const ConstPtr& object, float healthPerSecond)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
2014-10-12 00:13:24 +02:00
|
|
|
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
|
|
return;
|
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
std::vector<Ptr> actors;
|
2015-05-12 04:04:54 +02:00
|
|
|
mPhysics->getActorsCollidingWith(object, actors);
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const Ptr& actor : actors)
|
2014-07-29 19:01:40 +02:00
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
2014-10-11 22:21:48 +02:00
|
|
|
if (stats.isDead())
|
|
|
|
continue;
|
2017-03-25 22:40:11 +04:00
|
|
|
|
|
|
|
mPhysics->markAsNonSolid(object);
|
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
if (actor == getPlayerPtr() && mGodMode)
|
|
|
|
continue;
|
2017-03-25 22:40:11 +04:00
|
|
|
|
2014-07-29 19:01:40 +02:00
|
|
|
MWMechanics::DynamicStat<float> health = stats.getHealth();
|
|
|
|
health.setCurrent(health.getCurrent() - healthPerSecond * MWBase::Environment::get().getFrameDuration());
|
|
|
|
stats.setHealth(health);
|
2014-10-11 22:21:48 +02:00
|
|
|
|
|
|
|
if (healthPerSecond > 0.0f)
|
|
|
|
{
|
2015-03-11 23:07:39 +01:00
|
|
|
if (actor == getPlayerPtr())
|
2014-10-11 22:21:48 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
|
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
auto healthDamage = ESM::RefId::stringRefId("Health Damage");
|
|
|
|
if (!MWBase::Environment::get().getSoundManager()->getSoundPlaying(actor, healthDamage))
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWBase::Environment::get().getSoundManager()->playSound3D(actor, healthDamage, 1.0f, 1.0f);
|
2014-10-11 22:21:48 +02:00
|
|
|
}
|
2014-07-29 19:01:40 +02:00
|
|
|
}
|
2013-05-01 11:15:43 +02:00
|
|
|
}
|
2013-05-01 11:42:24 +02:00
|
|
|
|
|
|
|
float World::getWindSpeed()
|
|
|
|
{
|
|
|
|
if (isCellExterior() || isCellQuasiExterior())
|
2015-04-25 15:19:17 +02:00
|
|
|
return mWeatherManager->getWindSpeed();
|
2013-05-01 11:42:24 +02:00
|
|
|
else
|
|
|
|
return 0.f;
|
|
|
|
}
|
2013-05-11 18:38:27 +02:00
|
|
|
|
2014-06-24 18:37:38 +02:00
|
|
|
bool World::isInStorm() const
|
|
|
|
{
|
|
|
|
if (isCellExterior() || isCellQuasiExterior())
|
2015-04-25 15:19:17 +02:00
|
|
|
return mWeatherManager->isInStorm();
|
2014-06-24 18:37:38 +02:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-12 03:02:15 +02:00
|
|
|
osg::Vec3f World::getStormDirection() const
|
2014-06-24 18:37:38 +02:00
|
|
|
{
|
|
|
|
if (isCellExterior() || isCellQuasiExterior())
|
2015-04-25 15:19:17 +02:00
|
|
|
return mWeatherManager->getStormDirection();
|
2014-06-24 18:37:38 +02:00
|
|
|
else
|
2015-05-12 03:02:15 +02:00
|
|
|
return osg::Vec3f(0, 1, 0);
|
2014-06-24 18:37:38 +02:00
|
|
|
}
|
|
|
|
|
2015-12-06 20:43:50 +01:00
|
|
|
struct GetContainersOwnedByVisitor
|
|
|
|
{
|
2015-12-18 18:00:18 +01:00
|
|
|
GetContainersOwnedByVisitor(const MWWorld::ConstPtr& owner, std::vector<MWWorld::Ptr>& out)
|
2015-12-06 20:43:50 +01:00
|
|
|
: mOwner(owner)
|
|
|
|
, mOut(out)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-12-18 18:00:18 +01:00
|
|
|
MWWorld::ConstPtr mOwner;
|
2015-12-06 20:43:50 +01:00
|
|
|
std::vector<MWWorld::Ptr>& mOut;
|
|
|
|
|
|
|
|
bool operator()(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
|
|
|
if (ptr.getRefData().isDeleted())
|
|
|
|
return true;
|
|
|
|
|
2017-12-17 18:09:25 +04:00
|
|
|
// vanilla Morrowind does not allow to sell items from containers with zero capacity
|
|
|
|
if (ptr.getClass().getCapacity(ptr) <= 0.f)
|
2017-12-05 10:24:58 +04:00
|
|
|
return true;
|
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
if (ptr.getCellRef().getOwner() == mOwner.getCellRef().getRefId())
|
2015-12-06 20:43:50 +01:00
|
|
|
mOut.push_back(ptr);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-18 18:00:18 +01:00
|
|
|
void World::getContainersOwnedBy(const MWWorld::ConstPtr& owner, std::vector<MWWorld::Ptr>& out)
|
2013-05-11 18:38:27 +02:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2013-05-11 18:38:27 +02:00
|
|
|
{
|
2015-12-06 20:43:50 +01:00
|
|
|
GetContainersOwnedByVisitor visitor(owner, out);
|
2018-12-30 01:27:16 +03:00
|
|
|
cellstore->forEachType<ESM::Container>(visitor);
|
2013-05-11 18:38:27 +02:00
|
|
|
}
|
|
|
|
}
|
2013-05-27 02:18:36 +02:00
|
|
|
|
2015-12-18 18:00:18 +01:00
|
|
|
void World::getItemsOwnedBy(const MWWorld::ConstPtr& npc, std::vector<MWWorld::Ptr>& out)
|
2013-05-27 02:18:36 +02:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2013-05-27 02:18:36 +02:00
|
|
|
{
|
2020-02-11 22:24:18 +01:00
|
|
|
cellstore->forEach([&](const auto& ptr) {
|
2022-10-18 09:26:55 +02:00
|
|
|
if (ptr.getRefData().getBaseNode() && ptr.getCellRef().getOwner() == npc.getCellRef().getRefId())
|
2020-02-11 22:24:18 +01:00
|
|
|
out.push_back(ptr);
|
|
|
|
return true;
|
|
|
|
});
|
2013-05-27 02:18:36 +02:00
|
|
|
}
|
|
|
|
}
|
2014-04-29 15:27:49 +02:00
|
|
|
|
2015-12-18 18:02:57 +01:00
|
|
|
bool World::getLOS(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& targetActor)
|
2013-09-10 16:16:13 +02:00
|
|
|
{
|
2014-06-19 04:53:25 +02:00
|
|
|
if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled())
|
2014-03-07 19:33:46 +11:00
|
|
|
return false; // cannot get LOS unless both NPC's are enabled
|
2015-11-27 19:40:31 +01:00
|
|
|
if (!targetActor.getRefData().getBaseNode() || !actor.getRefData().getBaseNode())
|
2014-06-19 04:53:25 +02:00
|
|
|
return false; // not in active cell
|
2013-10-28 18:01:40 +01:00
|
|
|
|
2015-06-01 01:57:15 +02:00
|
|
|
return mPhysics->getLineOfSight(actor, targetActor);
|
2013-09-10 16:16:13 +02:00
|
|
|
}
|
|
|
|
|
2016-04-16 16:39:13 -05:00
|
|
|
float World::getDistToNearestRayHit(const osg::Vec3f& from, const osg::Vec3f& dir, float maxDist, bool includeWater)
|
2014-04-20 20:35:07 +04:00
|
|
|
{
|
2015-06-03 19:41:19 +02:00
|
|
|
osg::Vec3f to(dir);
|
|
|
|
to.normalize();
|
|
|
|
to = from + (to * maxDist);
|
2014-04-20 20:35:07 +04:00
|
|
|
|
2016-04-16 16:39:13 -05:00
|
|
|
int collisionTypes
|
|
|
|
= MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
|
|
|
|
if (includeWater)
|
|
|
|
{
|
|
|
|
collisionTypes |= MWPhysics::CollisionType_Water;
|
|
|
|
}
|
2020-08-03 22:44:16 +02:00
|
|
|
MWPhysics::RayCastingResult result
|
|
|
|
= mPhysics->castRay(from, to, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), collisionTypes);
|
2014-04-20 20:35:07 +04:00
|
|
|
|
2015-06-01 01:57:15 +02:00
|
|
|
if (!result.mHit)
|
|
|
|
return maxDist;
|
|
|
|
else
|
2015-06-03 19:41:19 +02:00
|
|
|
return (result.mHitPos - from).length();
|
2014-04-20 20:35:07 +04:00
|
|
|
}
|
|
|
|
|
2013-06-27 14:11:20 -07:00
|
|
|
void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable)
|
|
|
|
{
|
2015-05-12 19:02:56 +02:00
|
|
|
MWPhysics::Actor* physicActor = mPhysics->getActor(actor);
|
2014-07-27 23:10:58 +02:00
|
|
|
if (physicActor)
|
|
|
|
physicActor->enableCollisionBody(enable);
|
2013-06-27 14:11:20 -07:00
|
|
|
}
|
2013-07-07 15:03:06 +04:00
|
|
|
|
2023-01-19 17:31:45 +01:00
|
|
|
bool World::findInteriorPosition(std::string_view name, ESM::Position& pos)
|
2013-07-07 15:03:06 +04:00
|
|
|
{
|
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
|
|
|
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
|
|
|
|
2022-12-15 20:56:17 +01:00
|
|
|
MWWorld::CellStore* cellStore = mWorldModel.getInterior(name);
|
2013-07-07 15:03:06 +04:00
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
if (!cellStore)
|
2013-07-07 15:03:06 +04:00
|
|
|
return false;
|
2015-12-06 21:58:25 +01:00
|
|
|
|
2018-09-13 13:21:38 +04:00
|
|
|
std::vector<const MWWorld::CellRef*> sortedDoors;
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore->getReadOnlyDoors().mList)
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
if (!door.mRef.getTeleport())
|
2013-07-07 15:03:06 +04:00
|
|
|
continue;
|
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
sortedDoors.push_back(&door.mRef);
|
2018-09-13 13:21:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sort teleporting doors alphabetically, first by ID, then by destination cell to make search consistent
|
|
|
|
std::sort(sortedDoors.begin(), sortedDoors.end(), [](const MWWorld::CellRef* lhs, const MWWorld::CellRef* rhs) {
|
|
|
|
if (lhs->getRefId() != rhs->getRefId())
|
|
|
|
return lhs->getRefId() < rhs->getRefId();
|
|
|
|
|
|
|
|
return lhs->getDestCell() < rhs->getDestCell();
|
|
|
|
});
|
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const MWWorld::CellRef* door : sortedDoors)
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
MWWorld::CellStore* source = nullptr;
|
2013-07-07 15:03:06 +04:00
|
|
|
|
|
|
|
// door to exterior
|
2018-12-30 01:27:16 +03:00
|
|
|
if (door->getDestCell().empty())
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
ESM::Position doorDest = door->getDoorDest();
|
2022-05-23 19:21:44 +02:00
|
|
|
const osg::Vec2i index = positionToCellIndex(doorDest.pos[0], doorDest.pos[1]);
|
2022-12-15 20:56:17 +01:00
|
|
|
source = mWorldModel.getExterior(index.x(), index.y());
|
2013-07-07 15:03:06 +04:00
|
|
|
}
|
|
|
|
// door to interior
|
2018-09-13 13:21:38 +04:00
|
|
|
else
|
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
source = mWorldModel.getInterior(door->getDestCell());
|
2013-07-07 15:03:06 +04:00
|
|
|
}
|
2018-12-30 01:27:16 +03:00
|
|
|
if (source)
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
2013-07-07 15:03:06 +04:00
|
|
|
// Find door leading to our current teleport door
|
2016-10-02 17:48:54 +09:00
|
|
|
// and use its destination to position inside cell.
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const MWWorld::LiveCellRef<ESM::Door>& destDoor : source->getReadOnlyDoors().mList)
|
2018-09-13 13:21:38 +04:00
|
|
|
{
|
2022-12-01 19:37:35 +01:00
|
|
|
if (name == destDoor.mRef.getDestCell())
|
2013-07-07 15:03:06 +04:00
|
|
|
{
|
|
|
|
/// \note Using _any_ door pointed to the interior,
|
|
|
|
/// not the one pointed to current door.
|
2018-12-30 01:27:16 +03:00
|
|
|
pos = destDoor.mRef.getDoorDest();
|
2019-06-30 16:03:24 +03:00
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
2013-07-07 15:03:06 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-22 19:03:19 +01:00
|
|
|
for (const MWWorld::LiveCellRef<ESM4::Static>& stat4 : cellStore->getReadOnlyEsm4Statics().mList)
|
|
|
|
{
|
|
|
|
if (Misc::StringUtils::lowerCase(stat4.mBase->mEditorId) == "cocmarkerheading")
|
|
|
|
{
|
|
|
|
// found the COC position?
|
|
|
|
pos = stat4.mRef.getPosition();
|
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Fall back to the first static location.
|
|
|
|
const MWWorld::CellRefList<ESM4::Static>::List& statics4 = cellStore->getReadOnlyEsm4Statics().mList;
|
|
|
|
if (!statics4.empty())
|
|
|
|
{
|
|
|
|
pos = statics4.begin()->mRef.getPosition();
|
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
|
|
|
return true;
|
|
|
|
}
|
2015-02-04 12:06:29 -06:00
|
|
|
// Fall back to the first static location.
|
2018-12-30 01:27:16 +03:00
|
|
|
const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList;
|
|
|
|
if (!statics.empty())
|
|
|
|
{
|
2015-02-04 12:06:29 -06:00
|
|
|
pos = statics.begin()->mRef.getPosition();
|
2019-06-30 16:03:24 +03:00
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
2015-02-04 12:06:29 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-07 15:03:06 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-19 17:31:45 +01:00
|
|
|
bool World::findExteriorPosition(std::string_view nameId, ESM::Position& pos)
|
2013-07-07 15:03:06 +04:00
|
|
|
{
|
|
|
|
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
2022-12-24 01:56:27 +01:00
|
|
|
|
2023-01-27 01:13:17 +01:00
|
|
|
const MWWorld::Cell* ext = nullptr;
|
2022-12-24 01:56:27 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
ext = mWorldModel.getCell(nameId)->getCell();
|
|
|
|
if (!ext->isExterior())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
catch (std::exception&)
|
|
|
|
{
|
|
|
|
}
|
2022-08-27 13:07:59 +02:00
|
|
|
if (!ext)
|
|
|
|
{
|
2023-01-19 17:31:45 +01:00
|
|
|
size_t comma = nameId.find(',');
|
2022-08-27 13:07:59 +02:00
|
|
|
if (comma != std::string::npos)
|
2017-08-06 15:03:48 +02:00
|
|
|
{
|
2022-08-27 13:07:59 +02:00
|
|
|
int x, y;
|
2023-01-19 17:31:45 +01:00
|
|
|
std::from_chars_result xResult = std::from_chars(nameId.data(), nameId.data() + comma, x);
|
|
|
|
std::from_chars_result yResult
|
|
|
|
= std::from_chars(nameId.data() + comma + 1, nameId.data() + nameId.size(), y);
|
2022-08-27 13:07:59 +02:00
|
|
|
if (xResult.ec == std::errc::result_out_of_range || yResult.ec == std::errc::result_out_of_range)
|
|
|
|
throw std::runtime_error("Cell coordinates out of range.");
|
|
|
|
else if (xResult.ec == std::errc{} && yResult.ec == std::errc{})
|
2022-12-15 20:56:17 +01:00
|
|
|
ext = mWorldModel.getExterior(x, y)->getCell();
|
2022-08-27 13:07:59 +02:00
|
|
|
// ignore std::errc::invalid_argument, as this means that name probably refers to a interior cell
|
|
|
|
// instead of comma separated coordinates
|
2017-08-06 15:03:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
if (ext)
|
|
|
|
{
|
2013-07-07 15:03:06 +04:00
|
|
|
int x = ext->getGridX();
|
|
|
|
int y = ext->getGridY();
|
|
|
|
indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
|
|
|
|
|
2014-01-01 18:05:28 +01:00
|
|
|
// Note: Z pos will be adjusted by adjustPosition later
|
|
|
|
pos.pos[2] = 0;
|
2013-07-07 15:03:06 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2017-08-06 15:03:48 +02:00
|
|
|
|
2013-07-07 15:03:06 +04:00
|
|
|
return false;
|
|
|
|
}
|
2013-07-27 00:14:55 -07:00
|
|
|
|
|
|
|
void World::enableTeleporting(bool enable)
|
|
|
|
{
|
|
|
|
mTeleportEnabled = enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool World::isTeleportingEnabled() const
|
|
|
|
{
|
|
|
|
return mTeleportEnabled;
|
|
|
|
}
|
|
|
|
|
2013-10-02 15:12:41 +02:00
|
|
|
void World::enableLevitation(bool enable)
|
|
|
|
{
|
|
|
|
mLevitationEnabled = enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool World::isLevitationEnabled() const
|
|
|
|
{
|
|
|
|
return mLevitationEnabled;
|
|
|
|
}
|
|
|
|
|
2015-01-28 19:14:57 +01:00
|
|
|
void World::reattachPlayerCamera()
|
|
|
|
{
|
2015-05-21 23:54:39 +02:00
|
|
|
mRendering->rebuildPtr(getPlayerPtr());
|
2015-01-28 19:14:57 +01:00
|
|
|
}
|
|
|
|
|
2020-12-22 06:19:18 +03:00
|
|
|
bool World::getGodModeState() const
|
2013-09-12 08:30:00 -04:00
|
|
|
{
|
|
|
|
return mGodMode;
|
|
|
|
}
|
|
|
|
|
2013-08-24 21:19:12 -04:00
|
|
|
bool World::toggleGodMode()
|
|
|
|
{
|
2013-09-15 15:48:32 -04:00
|
|
|
mGodMode = !mGodMode;
|
2013-08-29 20:25:36 -04:00
|
|
|
|
|
|
|
return mGodMode;
|
2013-08-24 21:19:12 -04:00
|
|
|
}
|
|
|
|
|
2015-02-10 20:25:57 +01:00
|
|
|
bool World::toggleScripts()
|
|
|
|
{
|
|
|
|
mScriptsEnabled = !mScriptsEnabled;
|
|
|
|
return mScriptsEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool World::getScriptsEnabled() const
|
|
|
|
{
|
|
|
|
return mScriptsEnabled;
|
|
|
|
}
|
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
|
|
|
|
ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
|
2013-09-29 09:11:57 +02:00
|
|
|
{
|
2021-11-23 15:15:22 +01:00
|
|
|
GameContentLoader gameContentLoader;
|
2022-09-03 18:50:38 +02:00
|
|
|
EsmLoader esmLoader(mStore, mReaders, encoder, mESMVersions);
|
2021-11-13 22:37:53 +00:00
|
|
|
|
2021-11-23 15:15:22 +01:00
|
|
|
gameContentLoader.addLoader(".esm", esmLoader);
|
|
|
|
gameContentLoader.addLoader(".esp", esmLoader);
|
|
|
|
gameContentLoader.addLoader(".omwgame", esmLoader);
|
|
|
|
gameContentLoader.addLoader(".omwaddon", esmLoader);
|
|
|
|
gameContentLoader.addLoader(".project", esmLoader);
|
2021-11-13 22:37:53 +00:00
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
OMWScriptsLoader omwScriptsLoader(mStore);
|
2021-11-23 15:15:22 +01:00
|
|
|
gameContentLoader.addLoader(".omwscripts", omwScriptsLoader);
|
2021-11-13 22:37:53 +00:00
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
int idx = 0;
|
|
|
|
for (const std::string& file : content)
|
2013-09-29 09:11:57 +02:00
|
|
|
{
|
2022-07-03 00:02:29 +02:00
|
|
|
const auto filename = Files::pathFromUnicodeString(file);
|
|
|
|
const Files::MultiDirCollection& col
|
|
|
|
= fileCollections.getCollection(Files::pathToUnicodeString(filename.extension()));
|
2018-12-30 01:27:16 +03:00
|
|
|
if (col.doesExist(file))
|
2013-09-29 09:11:57 +02:00
|
|
|
{
|
2021-11-23 15:15:22 +01:00
|
|
|
gameContentLoader.load(col.getPath(file), idx, listener);
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
2014-04-08 20:19:09 +02:00
|
|
|
else
|
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
std::string message = "Failed loading " + file + ": the content file does not exist";
|
|
|
|
throw std::runtime_error(message);
|
2014-04-08 20:19:09 +02:00
|
|
|
}
|
2018-12-30 01:27:16 +03:00
|
|
|
idx++;
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
2022-06-01 22:53:18 +02:00
|
|
|
|
|
|
|
if (const auto v = esmLoader.getMasterFileFormat(); v.has_value() && *v == 0)
|
|
|
|
ensureNeededRecords(); // Insert records that may not be present in all versions of master files.
|
2013-09-29 09:11:57 +02:00
|
|
|
}
|
2013-11-09 10:54:51 +01:00
|
|
|
|
2022-06-03 18:59:08 +02:00
|
|
|
void World::loadGroundcoverFiles(const Files::Collections& fileCollections,
|
|
|
|
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
|
2021-10-29 16:47:17 +04:00
|
|
|
{
|
|
|
|
if (!Settings::Manager::getBool("enabled", "Groundcover"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
Log(Debug::Info) << "Loading groundcover:";
|
|
|
|
|
2022-06-03 18:59:08 +02:00
|
|
|
mGroundcoverStore.init(mStore.get<ESM::Static>(), fileCollections, groundcoverFiles, encoder, listener);
|
2021-10-29 16:47:17 +04:00
|
|
|
}
|
|
|
|
|
2022-07-29 16:00:25 +03:00
|
|
|
MWWorld::SpellCastState World::startSpellCast(const Ptr& actor)
|
2013-12-26 22:06:13 +01:00
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
|
|
|
|
2022-08-23 22:14:27 +02:00
|
|
|
std::string_view message;
|
2022-07-29 16:00:25 +03:00
|
|
|
MWWorld::SpellCastState result = MWWorld::SpellCastState::Success;
|
2014-01-08 18:39:44 +01:00
|
|
|
bool isPlayer = (actor == getPlayerPtr());
|
2013-12-26 22:06:13 +01:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& selectedSpell = stats.getSpells().getSelectedSpell();
|
2013-12-26 22:06:13 +01:00
|
|
|
|
|
|
|
if (!selectedSpell.empty())
|
|
|
|
{
|
2019-01-07 17:48:41 +03:00
|
|
|
const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);
|
2021-04-02 19:47:21 +02:00
|
|
|
int spellCost = MWMechanics::calcSpellCost(*spell);
|
2013-12-26 22:06:13 +01:00
|
|
|
|
|
|
|
// Check mana
|
2019-01-07 17:48:41 +03:00
|
|
|
bool godmode = (isPlayer && mGodMode);
|
2013-12-26 22:06:13 +01:00
|
|
|
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
|
2021-04-02 19:47:21 +02:00
|
|
|
if (spellCost > 0 && magicka.getCurrent() < spellCost && !godmode)
|
2013-12-26 22:06:13 +01:00
|
|
|
{
|
|
|
|
message = "#{sMagicInsufficientSP}";
|
2022-07-29 16:00:25 +03:00
|
|
|
result = MWWorld::SpellCastState::InsufficientMagicka;
|
2013-12-26 22:06:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If this is a power, check if it was already used in the last 24h
|
2022-07-29 16:00:25 +03:00
|
|
|
if (result == MWWorld::SpellCastState::Success && spell->mData.mType == ESM::Spell::ST_Power
|
|
|
|
&& !stats.getSpells().canUsePower(spell))
|
2013-12-26 22:06:13 +01:00
|
|
|
{
|
2014-07-21 23:56:38 +01:00
|
|
|
message = "#{sPowerAlreadyUsed}";
|
2022-07-29 16:00:25 +03:00
|
|
|
result = MWWorld::SpellCastState::PowerAlreadyUsed;
|
2013-12-26 22:06:13 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 16:00:25 +03:00
|
|
|
if (result == MWWorld::SpellCastState::Success && !godmode)
|
2014-01-14 03:34:11 +01:00
|
|
|
{
|
2022-07-29 16:00:25 +03:00
|
|
|
// Reduce mana
|
2021-04-02 19:47:21 +02:00
|
|
|
magicka.setCurrent(magicka.getCurrent() - spellCost);
|
2014-01-14 03:34:11 +01:00
|
|
|
stats.setMagicka(magicka);
|
2022-07-29 16:00:25 +03:00
|
|
|
|
|
|
|
// Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss)
|
|
|
|
static const float fFatigueSpellBase
|
|
|
|
= mStore.get<ESM::GameSetting>().find("fFatigueSpellBase")->mValue.getFloat();
|
|
|
|
static const float fFatigueSpellMult
|
|
|
|
= mStore.get<ESM::GameSetting>().find("fFatigueSpellMult")->mValue.getFloat();
|
|
|
|
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
|
|
|
|
const float normalizedEncumbrance = actor.getClass().getNormalizedEncumbrance(actor);
|
|
|
|
|
|
|
|
float fatigueLoss = spellCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
|
|
|
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
|
|
|
|
stats.setFatigue(fatigue);
|
2014-01-14 03:34:11 +01:00
|
|
|
}
|
2013-12-26 22:06:13 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 16:00:25 +03:00
|
|
|
if (isPlayer && result != MWWorld::SpellCastState::Success)
|
2013-12-26 22:06:13 +01:00
|
|
|
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
|
|
|
|
2022-07-29 16:00:25 +03:00
|
|
|
return result;
|
2013-12-26 22:06:13 +01:00
|
|
|
}
|
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
void World::castSpell(const Ptr& actor, bool manualSpell)
|
2013-11-09 07:51:46 +01:00
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
2013-11-17 23:15:57 +01:00
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
// 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;
|
2018-06-28 16:58:51 +04:00
|
|
|
if (!actor.isEmpty() && actor != MWMechanics::getPlayer() && !manualSpell)
|
2018-07-26 23:24:26 +03:00
|
|
|
stats.getAiSequence().getCombatTargets(targetActors);
|
2017-02-02 16:20:34 +09:00
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
const float fCombatDistance = mStore.get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat();
|
2015-08-16 02:06:04 +02:00
|
|
|
|
2018-05-15 18:57:36 +04:00
|
|
|
osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();
|
2015-08-16 02:06:04 +02:00
|
|
|
|
2018-05-15 18:57:36 +04:00
|
|
|
// for player we can take faced object first
|
|
|
|
MWWorld::Ptr target;
|
|
|
|
if (actor == MWMechanics::getPlayer())
|
|
|
|
target = getFacedObject();
|
2015-08-16 02:06:04 +02:00
|
|
|
|
2018-05-15 18:57:36 +04:00
|
|
|
// if the faced object can not be activated, do not use it
|
2019-09-10 21:56:10 +03:00
|
|
|
if (!target.isEmpty() && !target.getClass().hasToolTip(target))
|
2018-10-09 10:21:12 +04:00
|
|
|
target = nullptr;
|
2013-11-11 23:43:28 +01:00
|
|
|
|
2018-05-15 18:57:36 +04:00
|
|
|
if (target.isEmpty())
|
2015-08-16 02:06:04 +02:00
|
|
|
{
|
2018-06-28 16:58:51 +04:00
|
|
|
// For scripted spells we should not use hit contact
|
|
|
|
if (manualSpell)
|
|
|
|
{
|
|
|
|
if (actor != MWMechanics::getPlayer())
|
|
|
|
{
|
2020-05-17 00:29:21 +02:00
|
|
|
for (const auto& package : stats.getAiSequence())
|
2018-06-28 16:58:51 +04:00
|
|
|
{
|
2020-05-16 21:52:16 +02:00
|
|
|
if (package->getTypeId() == MWMechanics::AiPackageTypeId::Cast)
|
2018-06-28 16:58:51 +04:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
target = package->getTarget();
|
2018-06-28 16:58:51 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// For actor targets, we want to use hit contact with bounding boxes.
|
|
|
|
// This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would
|
|
|
|
// be very hard to aim at otherwise. For object targets, we want the detailed shapes (rendering
|
|
|
|
// raycast). If we used the bounding boxes for static objects, then we would not be able to target e.g.
|
|
|
|
// objects lying on a shelf.
|
|
|
|
std::pair<MWWorld::Ptr, osg::Vec3f> result1 = getHitContact(actor, fCombatDistance, targetActors);
|
2015-06-30 02:52:09 +02:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
// Get the target to use for "on touch" effects, using the facing direction from Head node
|
|
|
|
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
|
2016-09-21 23:34:32 +09:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1, 0, 0))
|
|
|
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, -1));
|
2018-05-15 18:57:36 +04:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
osg::Vec3f direction = orient * osg::Vec3f(0, 1, 0);
|
|
|
|
float distance = getMaxActivationDistance();
|
|
|
|
osg::Vec3f dest = origin + direction * distance;
|
2018-05-15 18:57:36 +04:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
|
2018-05-15 18:57:36 +04:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
float dist1 = std::numeric_limits<float>::max();
|
|
|
|
float dist2 = std::numeric_limits<float>::max();
|
2018-05-15 18:57:36 +04:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
|
|
|
dist1 = (origin - result1.second).length();
|
|
|
|
if (result2.mHit)
|
|
|
|
dist2 = (origin - result2.mHitPointWorld).length();
|
2018-05-15 18:57:36 +04:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
|
|
|
|
{
|
|
|
|
target = result1.first;
|
|
|
|
hitPosition = result1.second;
|
|
|
|
if (dist1 > getMaxActivationDistance())
|
2018-10-09 10:21:12 +04:00
|
|
|
target = nullptr;
|
2018-06-28 16:58:51 +04:00
|
|
|
}
|
|
|
|
else if (result2.mHit)
|
|
|
|
{
|
|
|
|
target = result2.mHitObject;
|
|
|
|
hitPosition = result2.mHitPointWorld;
|
2019-09-10 21:56:10 +03:00
|
|
|
if (dist2 > getMaxActivationDistance() && !target.isEmpty()
|
|
|
|
&& !target.getClass().hasToolTip(target))
|
2018-10-09 10:21:12 +04:00
|
|
|
target = nullptr;
|
2018-06-28 16:58:51 +04:00
|
|
|
}
|
2018-05-15 18:57:36 +04:00
|
|
|
}
|
2016-09-21 23:34:32 +09:00
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& selectedSpell = stats.getSpells().getSelectedSpell();
|
2013-11-09 07:51:46 +01:00
|
|
|
|
2018-06-28 16:58:51 +04:00
|
|
|
MWMechanics::CastSpell cast(actor, target, false, manualSpell);
|
2015-06-03 03:04:39 +02:00
|
|
|
cast.mHitPosition = hitPosition;
|
2013-11-09 07:51:46 +01:00
|
|
|
|
2013-11-17 23:15:57 +01:00
|
|
|
if (!selectedSpell.empty())
|
2013-11-09 07:51:46 +01:00
|
|
|
{
|
2019-01-07 17:48:41 +03:00
|
|
|
const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);
|
2013-11-17 23:15:57 +01:00
|
|
|
cast.cast(spell);
|
2013-11-10 15:40:31 +01:00
|
|
|
}
|
2014-01-19 11:42:58 +01:00
|
|
|
else if (actor.getClass().hasInventoryStore(actor))
|
2013-11-10 15:40:31 +01:00
|
|
|
{
|
2014-01-19 11:42:58 +01:00
|
|
|
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
|
|
|
if (inv.getSelectedEnchantItem() != inv.end())
|
2021-08-27 20:07:50 +02:00
|
|
|
{
|
|
|
|
const auto& itemPtr = *inv.getSelectedEnchantItem();
|
|
|
|
auto [slots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr);
|
|
|
|
int slot = 0;
|
|
|
|
for (std::size_t i = 0; i < slots.size(); ++i)
|
|
|
|
{
|
|
|
|
if (inv.getSlot(slots[i]) == inv.getSelectedEnchantItem())
|
|
|
|
{
|
|
|
|
slot = slots[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cast.cast(itemPtr, slot);
|
|
|
|
}
|
2013-11-09 07:51:46 +01:00
|
|
|
}
|
2013-11-13 14:02:15 +01:00
|
|
|
}
|
|
|
|
|
2018-08-24 15:03:54 +04:00
|
|
|
void World::launchProjectile(MWWorld::Ptr& actor, MWWorld::Ptr& projectile, const osg::Vec3f& worldPos,
|
|
|
|
const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength)
|
2014-03-07 06:11:00 +01:00
|
|
|
{
|
2018-08-24 15:03:54 +04:00
|
|
|
// An initial position of projectile can be outside shooter's collision box, so any object between shooter and
|
|
|
|
// launch position will be ignored. To avoid this issue, we should check for impact immediately before launch
|
|
|
|
// the projectile. So we cast a 1-yard-length ray from shooter to launch position and check if there are
|
|
|
|
// collisions in this area.
|
|
|
|
// TODO: as a better solutuon we should handle projectiles during physics update, not during world update.
|
|
|
|
const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0, -1, 0) * 64.f;
|
|
|
|
|
|
|
|
// Early out if the launch position is underwater
|
2022-05-06 20:36:25 +02:00
|
|
|
bool underwater = isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
|
2018-08-24 15:03:54 +04:00
|
|
|
if (underwater)
|
|
|
|
{
|
2021-08-31 16:25:45 +02:00
|
|
|
MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);
|
2018-08-24 15:03:54 +04:00
|
|
|
mRendering->emitWaterRipple(worldPos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer())
|
|
|
|
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
|
|
|
|
|
|
|
|
// Check for impact, if yes, handle hit, if not, launch projectile
|
2020-08-03 22:44:16 +02:00
|
|
|
MWPhysics::RayCastingResult result
|
|
|
|
= mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
|
2018-08-24 15:03:54 +04:00
|
|
|
if (result.mHit)
|
|
|
|
MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength);
|
|
|
|
else
|
|
|
|
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);
|
2014-03-07 06:11:00 +01:00
|
|
|
}
|
|
|
|
|
2021-08-27 20:07:50 +02:00
|
|
|
void World::launchMagicBolt(
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection, int slot)
|
2013-11-13 14:02:15 +01:00
|
|
|
{
|
2021-08-27 20:07:50 +02:00
|
|
|
mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection, slot);
|
2013-11-13 14:02:15 +01:00
|
|
|
}
|
2013-12-08 23:36:37 +01:00
|
|
|
|
2021-02-25 23:12:14 +00:00
|
|
|
void World::updateProjectilesCasters()
|
|
|
|
{
|
|
|
|
mProjectileManager->updateCasters();
|
|
|
|
}
|
|
|
|
|
2022-05-06 21:03:02 +02:00
|
|
|
void World::applyLoopingParticles(const MWWorld::Ptr& ptr) const
|
2018-03-17 13:41:13 +04:00
|
|
|
{
|
|
|
|
const MWWorld::Class& cls = ptr.getClass();
|
|
|
|
if (cls.isActor())
|
|
|
|
{
|
2021-08-27 20:07:50 +02:00
|
|
|
std::set<int> playing;
|
|
|
|
for (const auto& params : cls.getCreatureStats(ptr).getActiveSpells())
|
|
|
|
{
|
|
|
|
for (const auto& effect : params.getEffects())
|
|
|
|
{
|
|
|
|
if (playing.insert(effect.mEffectId).second)
|
|
|
|
{
|
|
|
|
const auto magicEffect = getStore().get<ESM::MagicEffect>().find(effect.mEffectId);
|
|
|
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx)
|
|
|
|
MWMechanics::playEffects(ptr, *magicEffect, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-17 13:41:13 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-25 13:00:05 +01:00
|
|
|
const std::vector<std::string>& World::getContentFiles() const
|
|
|
|
{
|
|
|
|
return mContentFiles;
|
|
|
|
}
|
2014-01-06 00:02:03 +01:00
|
|
|
|
2013-12-08 23:36:37 +01:00
|
|
|
void World::breakInvisibility(const Ptr& actor)
|
|
|
|
{
|
2021-08-27 20:07:50 +02:00
|
|
|
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(actor, ESM::MagicEffect::Invisibility);
|
2014-06-17 21:27:41 +02:00
|
|
|
|
|
|
|
// Normally updated once per frame, but here it is kinda important to do it right away.
|
|
|
|
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor);
|
2013-12-08 23:36:37 +01:00
|
|
|
}
|
2013-12-10 23:48:49 +01:00
|
|
|
|
2018-03-03 17:18:40 +04:00
|
|
|
bool World::useTorches() const
|
2013-12-10 23:48:49 +01:00
|
|
|
{
|
2018-03-03 17:18:40 +04:00
|
|
|
// If we are in exterior, check the weather manager.
|
|
|
|
// In interiors there are no precipitations and sun, so check the ambient
|
2018-03-19 23:08:15 +04:00
|
|
|
// Looks like pseudo-exteriors considered as interiors in this case
|
2014-04-28 18:33:42 +02:00
|
|
|
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
|
|
|
|
if (cell->isExterior())
|
2018-03-19 23:08:15 +04:00
|
|
|
{
|
|
|
|
float hour = getTimeStamp().getHour();
|
|
|
|
return mWeatherManager->useTorches(hour);
|
|
|
|
}
|
2014-04-28 18:33:42 +02:00
|
|
|
else
|
|
|
|
{
|
2023-01-31 19:50:48 +01:00
|
|
|
const MWWorld::Cell& cellVariant = *cell->getCell();
|
2023-01-27 01:13:17 +01:00
|
|
|
uint32_t ambient = cellVariant.getMood().mAmbiantColor;
|
2014-04-28 18:33:42 +02:00
|
|
|
int ambientTotal = (ambient & 0xff) + ((ambient >> 8) & 0xff) + ((ambient >> 16) & 0xff);
|
2023-01-22 23:40:55 +01:00
|
|
|
return !cell->getCell()->noSleep() && ambientTotal <= 201;
|
2014-04-28 18:33:42 +02:00
|
|
|
}
|
2013-12-31 18:35:46 +01:00
|
|
|
}
|
|
|
|
|
2022-09-19 19:05:22 +02:00
|
|
|
float World::getSunVisibility() const
|
|
|
|
{
|
|
|
|
return mWeatherManager->getSunVisibility();
|
|
|
|
}
|
|
|
|
|
|
|
|
float World::getSunPercentage() const
|
|
|
|
{
|
|
|
|
return mWeatherManager->getSunPercentage(getTimeStamp().getHour());
|
|
|
|
}
|
|
|
|
|
2016-02-05 00:25:47 +01:00
|
|
|
bool World::findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result)
|
2013-12-31 18:35:46 +01:00
|
|
|
{
|
2014-01-09 01:49:58 +01:00
|
|
|
if (cell->isExterior())
|
|
|
|
return false;
|
2013-12-31 18:35:46 +01:00
|
|
|
|
2015-02-12 22:27:47 -06:00
|
|
|
// Search for a 'nearest' exterior, counting each cell between the starting
|
|
|
|
// cell and the exterior as a distance of 1. Will fail for isolated interiors.
|
2023-01-19 17:31:45 +01:00
|
|
|
std::set<std::string_view> checkedCells;
|
|
|
|
std::set<std::string_view> currentCells;
|
|
|
|
std::set<std::string_view> nextCells;
|
2023-01-28 12:07:47 +01:00
|
|
|
nextCells.insert(cell->getCell()->getNameId());
|
2015-02-12 22:27:47 -06:00
|
|
|
|
|
|
|
while (!nextCells.empty())
|
|
|
|
{
|
|
|
|
currentCells = nextCells;
|
|
|
|
nextCells.clear();
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
for (const auto& currentCell : currentCells)
|
2019-01-07 20:35:36 +03:00
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
MWWorld::CellStore* next = mWorldModel.getInterior(currentCell);
|
2015-02-12 22:27:47 -06:00
|
|
|
if (!next)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Check if any door in the cell leads to an exterior directly
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const MWWorld::LiveCellRef<ESM::Door>& ref : next->getReadOnlyDoors().mList)
|
2015-02-12 22:27:47 -06:00
|
|
|
{
|
|
|
|
if (!ref.mRef.getTeleport())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ref.mRef.getDestCell().empty())
|
|
|
|
{
|
|
|
|
ESM::Position pos = ref.mRef.getDoorDest();
|
2015-06-03 19:41:19 +02:00
|
|
|
result = pos.asVec3();
|
2015-02-12 22:27:47 -06:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-19 17:31:45 +01:00
|
|
|
const std::string_view dest = ref.mRef.getDestCell();
|
2015-02-12 22:27:47 -06:00
|
|
|
if (!checkedCells.count(dest) && !currentCells.count(dest))
|
|
|
|
nextCells.insert(dest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-07 20:35:36 +03:00
|
|
|
checkedCells.insert(currentCell);
|
2013-12-31 18:35:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No luck :(
|
|
|
|
return false;
|
2013-12-10 23:48:49 +01:00
|
|
|
}
|
2014-01-01 02:22:11 +01:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWWorld::ConstPtr World::getClosestMarker(const MWWorld::ConstPtr& ptr, const ESM::RefId& id)
|
2014-01-01 02:22:11 +01:00
|
|
|
{
|
2015-02-24 20:51:57 -06:00
|
|
|
if (ptr.getCell()->isExterior())
|
|
|
|
{
|
|
|
|
return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id);
|
|
|
|
}
|
|
|
|
|
2015-02-14 15:43:09 -06:00
|
|
|
// Search for a 'nearest' marker, counting each cell between the starting
|
|
|
|
// cell and the exterior as a distance of 1. If an exterior is found, jump
|
|
|
|
// to the nearest exterior marker, without further interior searching.
|
2023-01-19 17:31:45 +01:00
|
|
|
std::set<std::string_view> checkedCells;
|
|
|
|
std::set<std::string_view> currentCells;
|
|
|
|
std::set<std::string_view> nextCells;
|
2015-12-17 23:59:18 +01:00
|
|
|
MWWorld::ConstPtr closestMarker;
|
2014-01-01 02:22:11 +01:00
|
|
|
|
2023-01-28 12:07:47 +01:00
|
|
|
nextCells.insert(ptr.getCell()->getCell()->getNameId());
|
2015-02-14 15:43:09 -06:00
|
|
|
while (!nextCells.empty())
|
|
|
|
{
|
|
|
|
currentCells = nextCells;
|
|
|
|
nextCells.clear();
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
for (const auto& cell : currentCells)
|
2018-12-30 01:27:16 +03:00
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
MWWorld::CellStore* next = mWorldModel.getInterior(cell);
|
2018-12-30 01:27:16 +03:00
|
|
|
checkedCells.insert(cell);
|
2015-02-14 15:43:09 -06:00
|
|
|
if (!next)
|
|
|
|
continue;
|
2014-01-09 01:49:58 +01:00
|
|
|
|
2015-12-17 22:37:18 +01:00
|
|
|
closestMarker = next->searchConst(id);
|
2015-02-21 15:55:22 -06:00
|
|
|
if (!closestMarker.isEmpty())
|
2015-02-14 15:43:09 -06:00
|
|
|
{
|
2015-02-21 15:55:22 -06:00
|
|
|
return closestMarker;
|
2015-02-14 15:43:09 -06:00
|
|
|
}
|
2014-01-01 02:22:11 +01:00
|
|
|
|
2015-02-14 15:43:09 -06:00
|
|
|
// Check if any door in the cell leads to an exterior directly
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const MWWorld::LiveCellRef<ESM::Door>& ref : next->getReadOnlyDoors().mList)
|
2015-02-14 15:43:09 -06:00
|
|
|
{
|
|
|
|
if (!ref.mRef.getTeleport())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ref.mRef.getDestCell().empty())
|
|
|
|
{
|
2015-06-03 19:41:19 +02:00
|
|
|
osg::Vec3f worldPos = ref.mRef.getDoorDest().asVec3();
|
2015-02-24 20:51:57 -06:00
|
|
|
return getClosestMarkerFromExteriorPosition(worldPos, id);
|
2015-02-14 15:43:09 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const auto& dest = ref.mRef.getDestCell();
|
2015-02-14 15:43:09 -06:00
|
|
|
if (!checkedCells.count(dest) && !currentCells.count(dest))
|
|
|
|
nextCells.insert(dest);
|
|
|
|
}
|
|
|
|
}
|
2014-01-01 02:22:11 +01:00
|
|
|
}
|
2015-02-14 15:43:09 -06:00
|
|
|
}
|
|
|
|
return MWWorld::Ptr();
|
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWWorld::ConstPtr World::getClosestMarkerFromExteriorPosition(const osg::Vec3f& worldPos, const ESM::RefId& id)
|
2022-08-27 13:07:59 +02:00
|
|
|
{
|
2015-12-17 23:59:18 +01:00
|
|
|
MWWorld::ConstPtr closestMarker;
|
2015-08-21 19:34:28 +12:00
|
|
|
float closestDistance = std::numeric_limits<float>::max();
|
2015-02-24 20:51:57 -06:00
|
|
|
|
|
|
|
std::vector<MWWorld::Ptr> markers;
|
2022-12-15 20:56:17 +01:00
|
|
|
mWorldModel.getExteriorPtrs(id, markers);
|
2018-12-30 01:27:16 +03:00
|
|
|
for (const Ptr& marker : markers)
|
2015-02-24 20:51:57 -06:00
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
osg::Vec3f markerPos = marker.getRefData().getPosition().asVec3();
|
2015-06-03 19:41:19 +02:00
|
|
|
float distance = (worldPos - markerPos).length2();
|
2015-02-24 20:51:57 -06:00
|
|
|
if (distance < closestDistance)
|
|
|
|
{
|
|
|
|
closestDistance = distance;
|
2018-12-30 01:27:16 +03:00
|
|
|
closestMarker = marker;
|
2015-02-24 20:51:57 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return closestMarker;
|
|
|
|
}
|
|
|
|
|
2019-01-25 20:04:35 +04:00
|
|
|
void World::rest(double hours)
|
2018-09-23 22:03:43 +04:00
|
|
|
{
|
2022-12-23 22:17:06 +01:00
|
|
|
mWorldModel.forEachLoadedCellStore([hours](CellStore& store) { store.rest(hours); });
|
2018-09-23 22:03:43 +04:00
|
|
|
}
|
2015-02-24 20:51:57 -06:00
|
|
|
|
2018-12-15 10:23:50 +04:00
|
|
|
void World::rechargeItems(double duration, bool activeOnly)
|
|
|
|
{
|
|
|
|
MWWorld::Ptr player = getPlayerPtr();
|
|
|
|
player.getClass().getInventoryStore(player).rechargeItems(duration);
|
|
|
|
|
|
|
|
if (activeOnly)
|
|
|
|
{
|
|
|
|
for (auto& cell : mWorldScene->getActiveCells())
|
|
|
|
{
|
|
|
|
cell->recharge(duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2022-12-23 22:17:06 +01:00
|
|
|
mWorldModel.forEachLoadedCellStore([duration](CellStore& store) { store.recharge(duration); });
|
2018-12-15 10:23:50 +04:00
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void World::teleportToClosestMarker(const MWWorld::Ptr& ptr, const ESM::RefId& id)
|
2015-02-14 15:43:09 -06:00
|
|
|
{
|
2015-12-17 23:59:18 +01:00
|
|
|
MWWorld::ConstPtr closestMarker = getClosestMarker(ptr, id);
|
2014-01-01 02:22:11 +01:00
|
|
|
|
2015-02-14 15:43:09 -06:00
|
|
|
if (closestMarker.isEmpty())
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Failed to teleport: no closest marker found";
|
2015-02-14 15:43:09 -06:00
|
|
|
return;
|
2014-01-01 02:22:11 +01:00
|
|
|
}
|
|
|
|
|
2023-01-19 17:31:45 +01:00
|
|
|
std::string_view cellName = "";
|
2015-02-14 15:43:09 -06:00
|
|
|
if (!closestMarker.mCell->isExterior())
|
2023-01-28 12:07:47 +01:00
|
|
|
cellName = closestMarker.mCell->getCell()->getNameId();
|
2015-02-14 15:43:09 -06:00
|
|
|
|
2023-01-19 17:31:45 +01:00
|
|
|
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);
|
2014-01-01 02:22:11 +01:00
|
|
|
action.execute(ptr);
|
2013-12-10 23:48:49 +01:00
|
|
|
}
|
2015-08-26 22:59:21 -05:00
|
|
|
|
2014-10-09 19:41:51 +13:00
|
|
|
void World::updateWeather(float duration, bool paused)
|
2013-12-29 12:47:44 +01:00
|
|
|
{
|
2018-03-19 23:08:15 +04:00
|
|
|
bool isExterior = isCellExterior() || isCellQuasiExterior();
|
2013-12-31 20:40:23 +01:00
|
|
|
if (mPlayer->wasTeleported())
|
|
|
|
{
|
|
|
|
mPlayer->setTeleported(false);
|
2018-03-19 23:08:15 +04:00
|
|
|
|
2023-01-22 23:40:55 +01:00
|
|
|
const ESM::RefId& playerRegion = getPlayerPtr().getCell()->getCell()->getRegion();
|
2018-03-19 23:08:15 +04:00
|
|
|
mWeatherManager->playerTeleported(playerRegion, isExterior);
|
2013-12-31 20:40:23 +01:00
|
|
|
}
|
2015-08-26 22:59:21 -05:00
|
|
|
|
2018-03-19 23:08:15 +04:00
|
|
|
const TimeStamp time = getTimeStamp();
|
|
|
|
mWeatherManager->update(duration, paused, time, isExterior);
|
2013-12-29 12:47:44 +01:00
|
|
|
}
|
2014-01-01 22:37:52 +01:00
|
|
|
|
2015-12-06 18:13:04 +01:00
|
|
|
struct AddDetectedReferenceVisitor
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
|
|
|
std::vector<Ptr>& mOut;
|
|
|
|
Ptr mDetector;
|
|
|
|
float mSquaredDist;
|
|
|
|
World::DetectionType mType;
|
2022-05-06 20:36:25 +02:00
|
|
|
const MWWorld::ESMStore& mStore;
|
|
|
|
|
2015-12-06 21:58:25 +01:00
|
|
|
bool operator()(const MWWorld::Ptr& ptr)
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
2015-06-03 21:37:21 +02:00
|
|
|
if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2()
|
|
|
|
>= mSquaredDist)
|
2014-01-01 22:37:52 +01:00
|
|
|
return true;
|
|
|
|
|
2015-05-06 02:12:55 +02:00
|
|
|
if (!ptr.getRefData().isEnabled() || ptr.getRefData().isDeleted())
|
2014-01-01 22:37:52 +01:00
|
|
|
return true;
|
|
|
|
|
2014-06-17 16:27:33 +02:00
|
|
|
// Consider references inside containers as well (except if we are looking for a Creature, they cannot be in
|
|
|
|
// containers)
|
2021-10-11 11:46:21 +00:00
|
|
|
bool isContainer = ptr.getClass().getType() == ESM::Container::sRecordId;
|
2019-05-04 14:09:52 +04:00
|
|
|
if (mType != World::Detect_Creature && (ptr.getClass().isActor() || isContainer))
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
2019-05-04 14:09:52 +04:00
|
|
|
// but ignore containers without resolved content
|
|
|
|
if (isContainer && ptr.getRefData().getCustomData() == nullptr)
|
2021-10-05 21:48:47 +02:00
|
|
|
{
|
|
|
|
for (const auto& containerItem : ptr.get<ESM::Container>()->mBase->mInventory.mList)
|
|
|
|
{
|
|
|
|
if (containerItem.mCount)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2022-05-06 20:36:25 +02:00
|
|
|
ManualRef ref(mStore, containerItem.mItem, containerItem.mCount);
|
2021-10-05 21:48:47 +02:00
|
|
|
if (needToAdd(ref.getPtr(), mDetector))
|
|
|
|
{
|
|
|
|
mOut.push_back(ptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception&)
|
|
|
|
{
|
|
|
|
// Ignore invalid item id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-04 14:09:52 +04:00
|
|
|
return true;
|
2021-10-05 21:48:47 +02:00
|
|
|
}
|
2019-05-04 14:09:52 +04:00
|
|
|
|
2014-01-01 22:37:52 +01:00
|
|
|
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
|
|
|
{
|
|
|
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
|
|
|
{
|
2014-06-17 16:27:33 +02:00
|
|
|
if (needToAdd(*it, mDetector))
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
|
|
|
mOut.push_back(ptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-17 16:27:33 +02:00
|
|
|
if (needToAdd(ptr, mDetector))
|
2014-01-01 22:37:52 +01:00
|
|
|
mOut.push_back(ptr);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-12-06 21:58:25 +01:00
|
|
|
bool needToAdd(const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector)
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
2014-06-17 16:27:33 +02:00
|
|
|
if (mType == World::Detect_Creature)
|
|
|
|
{
|
|
|
|
// If in werewolf form, this detects only NPCs, otherwise only creatures
|
|
|
|
if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf())
|
|
|
|
{
|
2021-10-11 11:46:21 +00:00
|
|
|
if (ptr.getClass().getType() != ESM::NPC::sRecordId)
|
2014-06-17 16:27:33 +02:00
|
|
|
return false;
|
|
|
|
}
|
2021-10-11 11:46:21 +00:00
|
|
|
else if (ptr.getClass().getType() != ESM::Creature::sRecordId)
|
2014-06-17 16:27:33 +02:00
|
|
|
return false;
|
2015-02-06 17:34:08 +01:00
|
|
|
|
|
|
|
if (ptr.getClass().getCreatureStats(ptr).isDead())
|
|
|
|
return false;
|
2014-06-17 16:27:33 +02:00
|
|
|
}
|
2014-01-01 22:37:52 +01:00
|
|
|
if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr))
|
|
|
|
return false;
|
|
|
|
if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void World::listDetectedReferences(const Ptr& ptr, std::vector<Ptr>& out, DetectionType type)
|
|
|
|
{
|
|
|
|
const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects();
|
|
|
|
float dist = 0;
|
|
|
|
if (type == World::Detect_Creature)
|
2014-08-16 22:38:22 +02:00
|
|
|
dist = effects.get(ESM::MagicEffect::DetectAnimal).getMagnitude();
|
2014-01-01 22:37:52 +01:00
|
|
|
else if (type == World::Detect_Key)
|
2014-08-16 22:38:22 +02:00
|
|
|
dist = effects.get(ESM::MagicEffect::DetectKey).getMagnitude();
|
2014-01-01 22:37:52 +01:00
|
|
|
else if (type == World::Detect_Enchantment)
|
2014-08-16 22:38:22 +02:00
|
|
|
dist = effects.get(ESM::MagicEffect::DetectEnchantment).getMagnitude();
|
2014-01-01 22:37:52 +01:00
|
|
|
|
|
|
|
if (!dist)
|
|
|
|
return;
|
|
|
|
|
2014-01-01 23:34:18 +01:00
|
|
|
dist = feetToGameUnits(dist);
|
2014-01-01 22:37:52 +01:00
|
|
|
|
2022-05-06 20:36:25 +02:00
|
|
|
AddDetectedReferenceVisitor visitor{ out, ptr, dist * dist, type, mStore };
|
2014-01-01 22:37:52 +01:00
|
|
|
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellStore : mWorldScene->getActiveCells())
|
2014-01-01 22:37:52 +01:00
|
|
|
{
|
2015-12-06 18:13:04 +01:00
|
|
|
cellStore->forEach(visitor);
|
2014-01-01 22:37:52 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-01 23:34:18 +01:00
|
|
|
|
|
|
|
float World::feetToGameUnits(float feet)
|
|
|
|
{
|
2018-09-17 14:52:43 +04:00
|
|
|
// Original engine rounds size upward
|
|
|
|
static const int unitsPerFoot = ceil(Constants::UnitsPerFoot);
|
|
|
|
return feet * unitsPerFoot;
|
2014-01-01 23:34:18 +01:00
|
|
|
}
|
2014-01-08 18:39:44 +01:00
|
|
|
|
2016-09-23 00:16:51 +09:00
|
|
|
float World::getActivationDistancePlusTelekinesis()
|
|
|
|
{
|
|
|
|
float telekinesisRangeBonus = mPlayer->getPlayer()
|
|
|
|
.getClass()
|
|
|
|
.getCreatureStats(mPlayer->getPlayer())
|
|
|
|
.getMagicEffects()
|
|
|
|
.get(ESM::MagicEffect::Telekinesis)
|
|
|
|
.getMagnitude();
|
|
|
|
telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus);
|
|
|
|
|
|
|
|
float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus;
|
|
|
|
|
|
|
|
return activationDistance;
|
|
|
|
}
|
|
|
|
|
2014-01-08 18:39:44 +01:00
|
|
|
MWWorld::Ptr World::getPlayerPtr()
|
|
|
|
{
|
|
|
|
return mPlayer->getPlayer();
|
|
|
|
}
|
2014-01-09 00:40:25 +01:00
|
|
|
|
2019-03-03 14:58:03 +03:00
|
|
|
MWWorld::ConstPtr World::getPlayerConstPtr() const
|
|
|
|
{
|
|
|
|
return mPlayer->getConstPlayer();
|
|
|
|
}
|
|
|
|
|
2014-01-09 00:40:25 +01:00
|
|
|
void World::updateDialogueGlobals()
|
|
|
|
{
|
|
|
|
MWWorld::Ptr player = getPlayerPtr();
|
|
|
|
int bounty = player.getClass().getNpcStats(player).getBounty();
|
|
|
|
int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId);
|
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
static float fCrimeGoldDiscountMult
|
|
|
|
= mStore.get<ESM::GameSetting>().find("fCrimeGoldDiscountMult")->mValue.getFloat();
|
|
|
|
static float fCrimeGoldTurnInMult
|
|
|
|
= mStore.get<ESM::GameSetting>().find("fCrimeGoldTurnInMult")->mValue.getFloat();
|
2014-01-09 00:40:25 +01:00
|
|
|
|
2015-03-08 13:07:29 +13:00
|
|
|
int discount = static_cast<int>(bounty * fCrimeGoldDiscountMult);
|
|
|
|
int turnIn = static_cast<int>(bounty * fCrimeGoldTurnInMult);
|
2014-01-09 00:40:25 +01:00
|
|
|
|
2014-07-19 23:49:54 +02:00
|
|
|
if (bounty > 0)
|
|
|
|
{
|
|
|
|
discount = std::max(1, discount);
|
|
|
|
turnIn = std::max(1, turnIn);
|
|
|
|
}
|
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
mGlobalVariables[Globals::sPCHasCrimeGold].setInteger((bounty <= playerGold) ? 1 : 0);
|
2014-01-09 00:40:25 +01:00
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
mGlobalVariables[Globals::sPCHasGoldDiscount].setInteger((discount <= playerGold) ? 1 : 0);
|
|
|
|
mGlobalVariables[Globals::sCrimeGoldDiscount].setInteger(discount);
|
2014-01-09 00:40:25 +01:00
|
|
|
|
2023-02-07 00:37:55 +01:00
|
|
|
mGlobalVariables[Globals::sCrimeGoldTurnIn].setInteger(turnIn);
|
|
|
|
mGlobalVariables[Globals::sPCHasTurnIn].setInteger((turnIn <= playerGold) ? 1 : 0);
|
2014-01-09 00:40:25 +01:00
|
|
|
}
|
2014-01-11 03:29:41 +01:00
|
|
|
|
|
|
|
void World::confiscateStolenItems(const Ptr& ptr)
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWWorld::ConstPtr prisonMarker = getClosestMarker(ptr, ESM::RefId::stringRefId("prisonmarker"));
|
2015-02-24 21:10:01 -06:00
|
|
|
if (prisonMarker.isEmpty())
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found.";
|
2015-02-24 21:10:01 -06:00
|
|
|
return;
|
|
|
|
}
|
2023-01-19 17:31:45 +01:00
|
|
|
std::string_view prisonName = prisonMarker.getCellRef().getDestCell();
|
2015-02-14 16:09:17 -06:00
|
|
|
if (prisonName.empty())
|
2014-01-11 03:29:41 +01:00
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior";
|
2015-02-14 16:09:17 -06:00
|
|
|
return;
|
|
|
|
}
|
2022-12-15 20:56:17 +01:00
|
|
|
MWWorld::CellStore* prison = mWorldModel.getInterior(prisonName);
|
2015-02-14 16:09:17 -06:00
|
|
|
if (!prison)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Failed to confiscate items: failed to load cell " << prisonName;
|
2015-02-14 16:09:17 -06:00
|
|
|
return;
|
2014-01-11 03:29:41 +01:00
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
MWWorld::Ptr closestChest = prison->search(ESM::RefId::stringRefId("stolen_goods"));
|
2014-04-23 13:20:43 -04:00
|
|
|
if (!closestChest.isEmpty()) // Found a close chest
|
2014-01-11 03:29:41 +01:00
|
|
|
{
|
2015-02-04 21:18:43 +01:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
|
2014-01-11 03:29:41 +01:00
|
|
|
}
|
2015-02-04 21:18:43 +01:00
|
|
|
else
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Failed to confiscate items: no stolen_goods container found";
|
2014-01-11 03:29:41 +01:00
|
|
|
}
|
2014-01-11 06:47:58 +01:00
|
|
|
|
|
|
|
void World::goToJail()
|
|
|
|
{
|
2022-01-16 10:15:47 +00:00
|
|
|
const MWWorld::Ptr player = getPlayerPtr();
|
2014-01-11 06:47:58 +01:00
|
|
|
if (!mGoToJail)
|
|
|
|
{
|
2014-09-09 00:00:55 +02:00
|
|
|
// Reset bounty and forget the crime now, but don't change cell yet (the player should be able to read the
|
|
|
|
// dialog text first)
|
2014-01-11 06:47:58 +01:00
|
|
|
mGoToJail = true;
|
2018-08-13 11:10:01 +04:00
|
|
|
mPlayerInJail = true;
|
2014-09-09 00:00:55 +02:00
|
|
|
|
|
|
|
int bounty = player.getClass().getNpcStats(player).getBounty();
|
|
|
|
player.getClass().getNpcStats(player).setBounty(0);
|
|
|
|
mPlayer->recordCrimeId();
|
|
|
|
confiscateStolenItems(player);
|
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
static int iDaysinPrisonMod = mStore.get<ESM::GameSetting>().find("iDaysinPrisonMod")->mValue.getInteger();
|
2014-09-09 00:00:55 +02:00
|
|
|
mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod);
|
|
|
|
|
2014-01-11 06:47:58 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-16 10:15:47 +00:00
|
|
|
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player))
|
|
|
|
{
|
2022-12-05 23:31:23 +01:00
|
|
|
player.getClass().getCreatureStats(player).setAttackingOrSpell(false);
|
2022-01-16 10:15:47 +00:00
|
|
|
}
|
|
|
|
|
2022-07-17 19:36:48 +03:00
|
|
|
mPlayer->setDrawState(MWMechanics::DrawState::Nothing);
|
2014-01-11 06:47:58 +01:00
|
|
|
mGoToJail = false;
|
|
|
|
|
2014-07-11 04:25:56 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
|
|
|
|
|
2015-02-07 04:05:28 +01:00
|
|
|
MWBase::Environment::get().getWindowManager()->goToJail(mDaysInPrison);
|
2014-01-11 06:47:58 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-14 07:40:17 +01:00
|
|
|
|
2016-01-05 23:27:42 +01:00
|
|
|
bool World::isPlayerInJail() const
|
|
|
|
{
|
2018-08-13 11:10:01 +04:00
|
|
|
return mPlayerInJail;
|
2016-01-05 23:27:42 +01:00
|
|
|
}
|
|
|
|
|
2018-08-13 08:30:50 +04:00
|
|
|
void World::setPlayerTraveling(bool traveling)
|
|
|
|
{
|
|
|
|
mPlayerTraveling = traveling;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool World::isPlayerTraveling() const
|
|
|
|
{
|
|
|
|
return mPlayerTraveling;
|
2016-01-05 23:27:42 +01:00
|
|
|
}
|
|
|
|
|
2016-11-16 20:15:25 +01:00
|
|
|
float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const
|
|
|
|
{
|
|
|
|
return mRendering->getTerrainHeightAt(worldPos);
|
|
|
|
}
|
|
|
|
|
2018-06-16 10:18:04 +04:00
|
|
|
osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const
|
2016-11-16 20:15:25 +01:00
|
|
|
{
|
2018-06-16 10:18:04 +04:00
|
|
|
if (!object.getClass().isActor())
|
|
|
|
return mRendering->getHalfExtents(object);
|
|
|
|
|
|
|
|
// Handle actors separately because of bodyparts
|
2016-11-16 20:15:25 +01:00
|
|
|
if (rendering)
|
2018-06-16 10:18:04 +04:00
|
|
|
return mPhysics->getRenderingHalfExtents(object);
|
2016-11-16 20:15:25 +01:00
|
|
|
else
|
2018-06-16 10:18:04 +04:00
|
|
|
return mPhysics->getHalfExtents(object);
|
2016-11-16 20:15:25 +01:00
|
|
|
}
|
|
|
|
|
2022-06-19 13:28:33 +02:00
|
|
|
std::filesystem::path World::exportSceneGraph(const Ptr& ptr)
|
2017-02-01 03:00:33 +01:00
|
|
|
{
|
2022-06-19 13:28:33 +02:00
|
|
|
auto file = mUserDataPath / "openmw.osgt";
|
2020-08-12 11:16:10 +04:00
|
|
|
if (!ptr.isEmpty())
|
|
|
|
{
|
|
|
|
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
|
|
|
|
mWorldScene->removeFromPagedRefs(ptr);
|
|
|
|
}
|
2017-02-01 03:00:33 +01:00
|
|
|
mRendering->exportSceneGraph(ptr, file, "Ascii");
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void World::spawnRandomCreature(const ESM::RefId& creatureList)
|
2014-01-14 07:40:17 +01:00
|
|
|
{
|
2019-01-07 17:48:41 +03:00
|
|
|
const ESM::CreatureLevList* list = mStore.get<ESM::CreatureLevList>().find(creatureList);
|
2014-01-14 07:40:17 +01:00
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
static int iNumberCreatures = mStore.get<ESM::GameSetting>().find("iNumberCreatures")->mValue.getInteger();
|
2022-05-17 17:39:00 +03:00
|
|
|
int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures, mPrng); // [1, iNumberCreatures]
|
2014-01-14 07:40:17 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < numCreatures; ++i)
|
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& selectedCreature = MWMechanics::getLevelledItem(list, true, mPrng);
|
2014-01-14 07:40:17 +01:00
|
|
|
if (selectedCreature.empty())
|
2019-01-07 17:48:41 +03:00
|
|
|
continue;
|
2014-01-14 07:40:17 +01:00
|
|
|
|
2019-01-07 17:48:41 +03:00
|
|
|
MWWorld::ManualRef ref(mStore, selectedCreature, 1);
|
2014-01-14 07:40:17 +01:00
|
|
|
|
2016-02-29 17:30:38 +01:00
|
|
|
safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f);
|
2014-01-14 07:40:17 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-17 10:52:44 +01:00
|
|
|
|
2015-04-19 17:55:56 +02:00
|
|
|
void World::spawnBloodEffect(const Ptr& ptr, const osg::Vec3f& worldPosition)
|
2014-01-17 10:52:44 +01:00
|
|
|
{
|
2015-03-11 23:07:39 +01:00
|
|
|
if (ptr == getPlayerPtr() && Settings::Manager::getBool("hit fader", "GUI"))
|
2014-10-06 18:33:41 +02:00
|
|
|
return;
|
|
|
|
|
2022-08-28 17:20:49 +02:00
|
|
|
std::string_view texture
|
|
|
|
= Fallback::Map::getString("Blood_Texture_" + std::to_string(ptr.getClass().getBloodTexture(ptr)));
|
2018-12-31 17:55:46 +03:00
|
|
|
if (texture.empty())
|
|
|
|
texture = Fallback::Map::getString("Blood_Texture_0");
|
|
|
|
|
2022-06-29 00:32:11 +02:00
|
|
|
std::string model = Misc::ResourceHelpers::correctMeshPath(
|
2022-08-28 17:20:49 +02:00
|
|
|
std::string{ Fallback::Map::getString("Blood_Model_" + std::to_string(Misc::Rng::rollDice(3))) }, // [0, 2]
|
2022-06-29 00:32:11 +02:00
|
|
|
mResourceSystem->getVFS());
|
2018-12-31 17:55:46 +03:00
|
|
|
|
2016-09-14 23:18:29 +09:00
|
|
|
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
|
2014-01-17 10:52:44 +01:00
|
|
|
}
|
2014-01-20 15:48:06 +01:00
|
|
|
|
2018-07-15 12:44:25 +04:00
|
|
|
void World::spawnEffect(const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos,
|
|
|
|
float scale, bool isMagicVFX)
|
2014-06-15 21:19:37 +02:00
|
|
|
{
|
2018-07-15 12:44:25 +04:00
|
|
|
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);
|
2014-06-15 21:19:37 +02:00
|
|
|
}
|
|
|
|
|
2014-06-13 00:01:29 +02:00
|
|
|
void World::activate(const Ptr& object, const Ptr& actor)
|
|
|
|
{
|
|
|
|
breakInvisibility(actor);
|
|
|
|
|
2016-05-25 21:37:56 +02:00
|
|
|
if (object.getRefData().activate())
|
2014-06-13 00:01:29 +02:00
|
|
|
{
|
2022-02-01 23:42:56 +00:00
|
|
|
MWBase::Environment::get().getLuaManager()->objectActivated(object, actor);
|
2022-04-06 17:06:55 +02:00
|
|
|
std::unique_ptr<MWWorld::Action> action = object.getClass().activate(object, actor);
|
2016-05-25 21:37:56 +02:00
|
|
|
action->execute(actor);
|
2014-06-13 00:01:29 +02:00
|
|
|
}
|
|
|
|
}
|
2014-08-30 17:55:35 +02:00
|
|
|
|
2015-12-06 18:13:04 +01:00
|
|
|
struct ResetActorsVisitor
|
2014-08-30 17:55:35 +02:00
|
|
|
{
|
2022-05-06 20:36:25 +02:00
|
|
|
World& mWorld;
|
|
|
|
|
2021-06-23 23:13:59 +02:00
|
|
|
bool operator()(const Ptr& ptr)
|
2014-08-30 17:55:35 +02:00
|
|
|
{
|
2015-01-11 12:20:22 +13:00
|
|
|
if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile())
|
2014-08-30 17:55:35 +02:00
|
|
|
{
|
2018-11-20 21:53:27 +04:00
|
|
|
if (ptr.getCell()->movedHere(ptr))
|
|
|
|
return true;
|
|
|
|
|
2014-08-30 17:55:35 +02:00
|
|
|
const ESM::Position& origPos = ptr.getCellRef().getPosition();
|
2022-05-06 20:36:25 +02:00
|
|
|
mWorld.moveObject(ptr, origPos.asVec3());
|
|
|
|
mWorld.rotateObject(ptr, origPos.asRotationVec3());
|
2018-11-20 21:53:27 +04:00
|
|
|
ptr.getClass().adjustPosition(ptr, true);
|
2014-08-30 17:55:35 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
2022-05-06 20:36:25 +02:00
|
|
|
|
2014-08-30 17:55:35 +02:00
|
|
|
void World::resetActors()
|
|
|
|
{
|
2018-12-30 01:27:16 +03:00
|
|
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
2014-08-30 17:55:35 +02:00
|
|
|
{
|
2022-05-06 20:36:25 +02:00
|
|
|
ResetActorsVisitor visitor{ *this };
|
2015-12-06 18:13:04 +01:00
|
|
|
cellstore->forEach(visitor);
|
2014-08-30 17:55:35 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-05 22:24:11 +02:00
|
|
|
|
2016-11-20 18:46:58 +01:00
|
|
|
bool World::isWalkingOnWater(const ConstPtr& actor) const
|
2014-10-05 22:24:11 +02:00
|
|
|
{
|
2015-12-18 17:36:14 +01:00
|
|
|
const MWPhysics::Actor* physicActor = mPhysics->getActor(actor);
|
2014-10-05 22:24:11 +02:00
|
|
|
if (physicActor && physicActor->isWalkingOnWater())
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-17 01:08:16 +02:00
|
|
|
|
2021-04-07 12:07:03 +04:00
|
|
|
osg::Vec3f World::aimToTarget(const ConstPtr& actor, const ConstPtr& target, bool isRangedCombat)
|
2015-09-17 01:08:16 +02:00
|
|
|
{
|
2016-06-10 23:30:41 +02:00
|
|
|
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
2021-04-07 12:07:03 +04:00
|
|
|
float heightRatio = isRangedCombat ? 2.f * Constants::TorsoHeight : 1.f;
|
|
|
|
weaponPos.z() += mPhysics->getHalfExtents(actor).z() * heightRatio;
|
|
|
|
osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target);
|
2015-09-17 01:08:16 +02:00
|
|
|
return (targetPos - weaponPos);
|
|
|
|
}
|
2015-11-18 19:00:43 +01:00
|
|
|
|
2015-12-18 17:36:14 +01:00
|
|
|
float World::getHitDistance(const ConstPtr& actor, const ConstPtr& target)
|
2015-11-18 19:00:43 +01:00
|
|
|
{
|
2016-06-10 23:30:41 +02:00
|
|
|
osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3();
|
|
|
|
osg::Vec3f halfExtents = mPhysics->getHalfExtents(actor);
|
2018-05-09 11:35:51 +04:00
|
|
|
weaponPos.z() += halfExtents.z();
|
2016-06-10 23:30:41 +02:00
|
|
|
|
|
|
|
return mPhysics->getHitDistance(weaponPos, target) - halfExtents.y();
|
2015-11-18 19:00:43 +01:00
|
|
|
}
|
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void preload(MWWorld::Scene* scene, const ESMStore& store, const ESM::RefId& obj)
|
2017-02-15 00:55:35 +01:00
|
|
|
{
|
|
|
|
if (obj.empty())
|
|
|
|
return;
|
2017-02-20 19:58:00 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
MWWorld::ManualRef ref(store, obj);
|
|
|
|
std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
|
|
|
|
if (!model.empty())
|
|
|
|
scene->preload(model, ref.getPtr().getClass().useAnim());
|
|
|
|
}
|
2021-04-19 15:43:00 +04:00
|
|
|
catch (std::exception&)
|
2017-02-20 19:58:00 +01:00
|
|
|
{
|
|
|
|
}
|
2017-02-15 00:55:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void World::preloadEffects(const ESM::EffectList* effectList)
|
|
|
|
{
|
2019-03-07 12:38:55 +04:00
|
|
|
for (const ESM::ENAMstruct& effectInfo : effectList->mList)
|
2017-02-15 00:55:35 +01:00
|
|
|
{
|
2019-03-07 12:38:55 +04:00
|
|
|
const ESM::MagicEffect* effect = mStore.get<ESM::MagicEffect>().find(effectInfo.mEffectID);
|
2017-02-15 00:55:35 +01:00
|
|
|
|
2019-03-07 12:38:55 +04:00
|
|
|
if (MWMechanics::isSummoningEffect(effectInfo.mEffectID))
|
2017-02-20 19:58:00 +01:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
preload(mWorldScene.get(), mStore, ESM::RefId::stringRefId("VFX_Summon_Start"));
|
2019-03-07 12:38:55 +04:00
|
|
|
preload(mWorldScene.get(), mStore, MWMechanics::getSummonedCreature(effectInfo.mEffectID));
|
2017-02-20 19:58:00 +01:00
|
|
|
}
|
|
|
|
|
2018-01-30 22:05:16 +00:00
|
|
|
preload(mWorldScene.get(), mStore, effect->mCasting);
|
|
|
|
preload(mWorldScene.get(), mStore, effect->mHit);
|
2017-02-15 00:55:35 +01:00
|
|
|
|
2019-03-07 12:38:55 +04:00
|
|
|
if (effectInfo.mArea > 0)
|
2018-01-30 22:05:16 +00:00
|
|
|
preload(mWorldScene.get(), mStore, effect->mArea);
|
2019-03-07 12:38:55 +04:00
|
|
|
if (effectInfo.mRange == ESM::RT_Target)
|
2018-01-30 22:05:16 +00:00
|
|
|
preload(mWorldScene.get(), mStore, effect->mBolt);
|
2017-02-15 00:55:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 01:49:08 +03:00
|
|
|
DetourNavigator::Navigator* World::getNavigator() const
|
|
|
|
{
|
|
|
|
return mNavigator.get();
|
|
|
|
}
|
|
|
|
|
2018-07-21 13:37:02 +03:00
|
|
|
void World::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
|
2022-06-17 00:28:44 +02:00
|
|
|
const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const
|
2018-07-21 13:37:02 +03:00
|
|
|
{
|
2022-06-17 00:28:44 +02:00
|
|
|
mRendering->updateActorPath(actor, path, agentBounds, start, end);
|
2018-07-21 13:37:02 +03:00
|
|
|
}
|
|
|
|
|
2018-11-03 10:42:14 +04:00
|
|
|
void World::removeActorPath(const MWWorld::ConstPtr& actor) const
|
|
|
|
{
|
|
|
|
mRendering->removeActorPath(actor);
|
|
|
|
}
|
|
|
|
|
2018-08-31 01:39:44 +03:00
|
|
|
void World::setNavMeshNumberToRender(const std::size_t value)
|
|
|
|
{
|
|
|
|
mRendering->setNavMeshNumber(value);
|
|
|
|
}
|
|
|
|
|
2022-06-17 00:28:44 +02:00
|
|
|
DetourNavigator::AgentBounds World::getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const
|
2019-03-03 14:58:03 +03:00
|
|
|
{
|
2022-06-17 00:28:44 +02:00
|
|
|
const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
|
2022-07-10 17:27:00 +02:00
|
|
|
if (physicsActor == nullptr || !actor.isInCell() || actor.getCell()->isExterior())
|
2022-06-04 00:44:42 +02:00
|
|
|
return DetourNavigator::AgentBounds{ mDefaultActorCollisionShapeType, mDefaultHalfExtents };
|
2019-03-03 14:58:03 +03:00
|
|
|
else
|
2022-06-17 00:28:44 +02:00
|
|
|
return DetourNavigator::AgentBounds{ physicsActor->getCollisionShapeType(),
|
|
|
|
physicsActor->getHalfExtents() };
|
2019-03-03 14:58:03 +03:00
|
|
|
}
|
|
|
|
|
2019-08-15 19:19:04 +02:00
|
|
|
bool World::hasCollisionWithDoor(
|
|
|
|
const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const
|
|
|
|
{
|
|
|
|
const auto object = mPhysics->getObject(door);
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
btVector3 aabbMin;
|
|
|
|
btVector3 aabbMax;
|
2021-10-30 03:30:38 +02:00
|
|
|
object->getShapeInstance()->mCollisionShape->getAabb(btTransform::getIdentity(), aabbMin, aabbMax);
|
2019-08-15 19:19:04 +02:00
|
|
|
|
2020-10-14 15:55:15 +02:00
|
|
|
const auto toLocal = object->getTransform().inverse();
|
2019-08-15 19:19:04 +02:00
|
|
|
const auto localFrom = toLocal(Misc::Convert::toBullet(position));
|
|
|
|
const auto localTo = toLocal(Misc::Convert::toBullet(destination));
|
|
|
|
|
|
|
|
btScalar hitDistance = 1;
|
|
|
|
btVector3 hitNormal;
|
|
|
|
return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal);
|
|
|
|
}
|
2020-02-09 18:24:08 +01:00
|
|
|
|
2021-09-29 00:42:29 +02:00
|
|
|
bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
|
2022-08-01 00:28:14 +02:00
|
|
|
std::span<const MWWorld::ConstPtr> ignore, std::vector<MWWorld::Ptr>* occupyingActors) const
|
2020-02-09 18:24:08 +01:00
|
|
|
{
|
2021-09-29 00:42:29 +02:00
|
|
|
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors);
|
2020-02-09 18:24:08 +01:00
|
|
|
}
|
2020-05-22 00:11:23 +02:00
|
|
|
|
|
|
|
void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
|
|
|
{
|
2022-08-28 14:52:12 +02:00
|
|
|
DetourNavigator::reportStats(mNavigator->getStats(), frameNumber, stats);
|
2020-05-22 00:11:23 +02:00
|
|
|
mPhysics->reportStats(frameNumber, stats);
|
|
|
|
}
|
2020-06-03 11:32:28 +04:00
|
|
|
|
|
|
|
void World::updateSkyDate()
|
|
|
|
{
|
|
|
|
ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp();
|
|
|
|
mRendering->skySetDate(currentDate.mDay, currentDate.mMonth);
|
|
|
|
}
|
2020-10-20 09:22:43 +00:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
std::vector<MWWorld::Ptr> World::getAll(const ESM::RefId& id)
|
2020-10-20 09:22:43 +00:00
|
|
|
{
|
2022-12-15 20:56:17 +01:00
|
|
|
return mWorldModel.getAll(id);
|
2020-10-20 09:22:43 +00:00
|
|
|
}
|
2022-03-06 20:02:03 +02:00
|
|
|
|
|
|
|
Misc::Rng::Generator& World::getPrng()
|
|
|
|
{
|
|
|
|
return mPrng;
|
|
|
|
}
|
|
|
|
|
2022-05-13 18:58:00 -07:00
|
|
|
MWRender::PostProcessor* World::getPostProcessor()
|
|
|
|
{
|
|
|
|
return mRendering->getPostProcessor();
|
|
|
|
}
|
2022-05-25 20:41:11 +02:00
|
|
|
|
|
|
|
void World::setActorActive(const MWWorld::Ptr& ptr, bool value)
|
|
|
|
{
|
|
|
|
if (MWPhysics::Actor* const actor = mPhysics->getActor(ptr))
|
|
|
|
actor->setActive(value);
|
|
|
|
}
|
2010-07-02 09:38:22 +02:00
|
|
|
}
|