1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-30 12:32:36 +00:00
OpenMW/apps/openmw/mwworld/worldimp.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

3824 lines
137 KiB
C++
Raw Normal View History

#include "worldimp.hpp"
2014-06-14 13:54:01 +02:00
2022-08-27 13:07:59 +02:00
#include <charconv>
#include <vector>
2022-08-27 13:07:59 +02:00
2015-05-22 01:43:45 +02:00
#include <osg/ComputeBoundsVisitor>
#include <osg/Group>
#include <osg/Timer>
#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>
#include <components/esm3/cellref.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/esm3/loadclas.hpp>
2022-09-08 21:08:59 +02:00
#include <components/esm3/loadcrea.hpp>
#include <components/esm3/loadench.hpp>
#include <components/esm3/loadgmst.hpp>
#include <components/esm3/loadlevlist.hpp>
#include <components/esm3/loadmgef.hpp>
2022-09-08 21:08:59 +02:00
#include <components/esm3/loadregn.hpp>
#include <components/esm3/loadstat.hpp>
#include <components/esm4/loadcell.hpp>
#include <components/esm4/loaddoor.hpp>
#include <components/esm4/loadstat.hpp>
2015-07-07 19:16:32 +02:00
#include <components/misc/constants.hpp>
#include <components/misc/convert.hpp>
#include <components/misc/mathutil.hpp>
#include <components/misc/resourcehelpers.hpp>
2015-04-22 17:58:55 +02:00
#include <components/misc/rng.hpp>
#include <components/files/collections.hpp>
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>
2022-05-14 22:53:53 -07:00
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/workqueue.hpp>
2022-08-12 00:09:49 +02:00
#include <components/detournavigator/agentbounds.hpp>
#include <components/detournavigator/debug.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/detournavigator/settings.hpp>
#include <components/detournavigator/stats.hpp>
#include <components/detournavigator/updateguard.hpp>
2018-03-14 01:49:08 +03:00
#include <components/files/conversion.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
2023-06-27 23:41:06 +02:00
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/luamanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/levelledlist.hpp"
#include "../mwmechanics/npcstats.hpp"
2013-11-16 02:34:43 +01:00
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwmechanics/summoning.hpp"
2015-05-22 04:36:17 +02:00
#include "../mwrender/animation.hpp"
2015-05-21 23:54:39 +02:00
#include "../mwrender/camera.hpp"
#include "../mwrender/npcanimation.hpp"
2022-05-13 18:58:00 -07:00
#include "../mwrender/postprocessor.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwscript/globalscripts.hpp"
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"
#include "../mwphysics/physicssystem.hpp"
#include "actionteleport.hpp"
#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"
#include "inventorystore.hpp"
#include "manualref.hpp"
#include "player.hpp"
2015-06-01 21:41:13 +02:00
#include "projectilemanager.hpp"
#include "weather.hpp"
2010-11-06 13:11:09 -04:00
#include "contentloader.hpp"
#include "esmloader.hpp"
2010-07-03 15:41:20 +02:00
namespace MWWorld
{
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" } };
}
}
struct GameContentLoader : public ContentLoader
{
void addLoader(std::string&& extension, ContentLoader& loader)
{
mLoaders.emplace(std::move(extension), &loader);
}
void load(const std::filesystem::path& filepath, int& index, Loading::Listener* listener) override
{
const auto it
= mLoaders.find(Misc::StringUtils::lowerCase(Files::pathToUnicodeString(filepath.extension())));
if (it != mLoaders.end())
{
const auto filename = filepath.filename();
Log(Debug::Info) << "Loading content file " << filename;
if (listener != nullptr)
listener->setLabel(MyGUI::TextIterator::toTagsString(Files::pathToUnicodeString(filename)));
it->second->load(filepath, index, listener);
}
else
{
std::string msg("Cannot load file: ");
msg += Files::pathToUnicodeString(filepath);
throw std::runtime_error(msg.c_str());
}
}
private:
std::map<std::string, ContentLoader*> mLoaders;
};
struct OMWScriptsLoader : public ContentLoader
{
ESMStore& mStore;
OMWScriptsLoader(ESMStore& store)
: mStore(store)
{
}
void load(const std::filesystem::path& filepath, int& /*index*/, Loading::Listener* /*listener*/) override
{
mStore.addOMWScripts(filepath);
}
};
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
{
updateSkyDate();
mRendering->setSkyEnabled(true);
2010-08-22 20:55:22 +02:00
}
2012-02-26 18:21:11 +01:00
else
mRendering->setSkyEnabled(false);
2010-08-22 20:55:22 +02:00
}
World::World(Resource::ResourceSystem* resourceSystem, int activationDistanceOverride, const std::string& startCell,
2023-03-19 00:45:50 +01:00
const std::filesystem::path& userDataPath)
: mResourceSystem(resourceSystem)
, mLocalScripts(mStore)
2022-12-15 20:56:17 +01:00
, mWorldModel(mStore, mReaders)
, mTimeManager(std::make_unique<DateTimeManager>())
, 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)
, mUserDataPath(userDataPath)
, mActivationDistanceOverride(activationDistanceOverride)
, mStartCell(startCell)
2023-07-25 14:39:03 +02:00
, mSwimHeightScale(0.f)
, mDistanceToFacedObject(-1.f)
, mTeleportEnabled(true)
, mLevitationEnabled(true)
, mGoToJail(false)
, mDaysInPrison(0)
, mPlayerTraveling(false)
, mPlayerInJail(false)
, mSpellPreloadTimer(0.f)
{
}
void World::loadData(const Files::Collections& fileCollections, const std::vector<std::string>& contentFiles,
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
{
mContentFiles = contentFiles;
if (encoder)
mReaders.setStatelessEncoder(encoder->getStatelessEncoder());
mESMVersions.resize(mContentFiles.size(), -1);
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
fillGlobalVariables();
mStore.setUp();
mStore.validateRecords(mReaders);
2018-07-20 22:11:34 +03:00
mStore.movePlayerRecord();
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
}
2018-07-20 22:11:34 +03:00
void World::init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,
SceneUtil::UnrefQueue& unrefQueue)
{
mPhysics = std::make_unique<MWPhysics::PhysicsSystem>(mResourceSystem, rootNode);
if (Settings::navigator().mEnable)
{
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale;
mNavigator = DetourNavigator::makeNavigator(navigatorSettings, mUserDataPath);
}
else
{
mNavigator = DetourNavigator::makeNavigatorStub();
}
2018-03-14 01:49:08 +03:00
mRendering = std::make_unique<MWRender::RenderingManager>(
viewer, rootNode, mResourceSystem, workQueue, *mNavigator, mGroundcoverStore, unrefQueue);
2022-05-29 13:24:48 +02:00
mProjectileManager = std::make_unique<ProjectileManager>(
mRendering->getLightRoot()->asGroup(), mResourceSystem, mRendering.get(), mPhysics.get());
mRendering->preloadCommonAssets();
2022-05-29 13:24:48 +02:00
mWeatherManager = std::make_unique<MWWorld::WeatherManager>(*mRendering, mStore);
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
}
void World::fillGlobalVariables()
{
mGlobalVariables.fill(mStore);
mTimeManager->setup(mGlobalVariables);
}
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
mGodMode = false;
mScriptsEnabled = true;
mSky = true;
2013-05-15 17:54:18 +02:00
// Rebuild player
setupPlayer();
renderPlayer();
mRendering->getCamera()->reset();
2013-05-15 17:54:18 +02: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.
mWeatherManager.reset();
2022-05-29 13:24:48 +02:00
mWeatherManager = std::make_unique<MWWorld::WeatherManager>(*mRendering.get(), mStore);
2014-05-19 14:09:16 +02:00
if (!bypass)
{
// set new game mark
mGlobalVariables[Globals::sCharGenState].setInteger(1);
2014-05-19 14:09:16 +02:00
}
else
mGlobalVariables[Globals::sCharGenState].setInteger(-1);
2014-05-19 14:09:16 +02:00
if (bypass && !mStartCell.empty())
{
ESM::Position pos;
ESM::RefId cellId = findExteriorPosition(mStartCell, pos);
if (!cellId.empty())
{
changeToCell(cellId, pos, true);
adjustPosition(getPlayerPtr(), false);
}
else
{
findInteriorPosition(mStartCell, pos);
changeToInteriorCell(mStartCell, pos, true);
}
}
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;
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;
ESM::ExteriorCellLocation exteriorCellPos = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1]);
ESM::RefId cellId = ESM::RefId::esm3ExteriorCell(exteriorCellPos.mX, exteriorCellPos.mY);
mWorldScene->changeToExteriorCell(cellId, pos, true);
2014-05-19 14:09:16 +02: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())
2023-08-18 11:03:37 +04:00
{
2023-09-12 14:50:19 +04:00
// Make sure that we do not continue to play a Title music after a new game video.
2023-08-18 11:03:37 +04:00
MWBase::Environment::get().getSoundManager()->stopMusic();
2023-09-12 14:50:19 +04:00
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
2015-05-23 05:42:21 +02:00
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
2023-08-18 11:03:37 +04:00
}
2015-05-23 05:42:21 +02:00
}
2014-05-20 09:45:39 +02:00
// enable collision
2015-05-23 06:07:11 +02:00
if (!mPhysics->toggleCollisionMode())
mPhysics->toggleCollisionMode();
MWBase::Environment::get().getWindowManager()->updatePlayer();
mTimeManager->setup(mGlobalVariables);
// Initial seed.
mPrng.seed(mRandomSeed);
}
2012-04-04 16:08:46 +02:00
void World::clear()
{
mWeatherManager->clear();
2015-04-19 17:55:56 +02:00
mRendering->clear();
2014-05-17 05:21:17 +02:00
mProjectileManager->clear();
mLocalScripts.clear();
mWorldScene->clear();
mWorldModel.clear();
mStore.clearDynamic();
if (mPlayer)
2013-12-07 13:17:28 +01:00
{
mPlayer->clear();
2020-11-13 11:39:47 +04:00
mPlayer->setCell(nullptr);
mPlayer->getPlayer().getRefData() = RefData();
mPlayer->set(mStore.get<ESM::NPC>().find(ESM::RefId::stringRefId("Player")));
2013-12-07 13:17:28 +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;
mLevitationEnabled = true;
mPlayerTraveling = false;
mPlayerInJail = false;
fillGlobalVariables();
}
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()
+ 1 // player record
2014-05-14 09:47:49 +02:00
+ 1 // weather record
+ 1 // actorId counter
+ 1 // levitation/teleport enabled state
+ 1 // camera
+ 1; // random state.
2013-12-07 13:17:28 +01:00
}
int World::countSavedGameCells() const
{
2022-12-15 20:56:17 +01:00
return mWorldModel.countSavedGameRecords();
}
void World::write(ESM::ESMWriter& writer, Loading::Listener& progress) const
2013-12-07 13:17:28 +01:00
{
writer.startRecord(ESM::REC_RAND);
writer.writeHNOString("RAND", Misc::Rng::serialize(mPrng));
writer.endRecord(ESM::REC_RAND);
// 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())
{
MWBase::Environment::get().getWindowManager()->writeFog(cellstore);
}
2014-05-14 09:47:49 +02:00
MWMechanics::CreatureStats::writeActorIdCounter(writer);
mStore.write(writer, progress); // dynamic Store must be written (and read) before Cells, so that
// references to custom made records will be recognized
mPlayer->write(writer, progress);
2022-12-15 20:56:17 +01:00
mWorldModel.write(writer, progress);
mGlobalVariables.write(writer, progress);
mWeatherManager->write(writer, progress);
2014-05-17 05:21:17 +02:00
mProjectileManager->write(writer, progress);
2015-06-01 21:41:13 +02:00
writer.startRecord(ESM::REC_ENAB);
writer.writeHNT("TELE", mTeleportEnabled);
writer.writeHNT("LEVT", mLevitationEnabled);
writer.endRecord(ESM::REC_ENAB);
writer.startRecord(ESM::REC_CAM_);
writer.writeHNT("FIRS", isFirstPerson());
writer.endRecord(ESM::REC_CAM_);
2013-12-07 13:17:28 +01:00
}
void World::readRecord(ESM::ESMReader& reader, uint32_t type)
2013-12-07 13:17:28 +01:00
{
switch (type)
2013-12-07 13:17:28 +01:00
{
case ESM::REC_ACTC:
MWMechanics::CreatureStats::readActorIdCounter(reader);
return;
case ESM::REC_ENAB:
reader.getHNT(mTeleportEnabled, "TELE");
reader.getHNT(mLevitationEnabled, "LEVT");
return;
case ESM::REC_RAND:
{
auto data = reader.getHNOString("RAND");
Misc::Rng::deserialize(data, mPrng);
}
break;
case ESM::REC_PLAY:
2023-06-05 22:19:24 +02:00
// World::write always puts `ESM::REC_PLAY` between ESMStore (that contains dynamic records)
// and WorldModel (that can contain instances of dynamic records). Here we need to rebuild
// ESMStore index in order to be able to lookup dynamic records while loading the player and
// WorldModel.
mStore.rebuildIdsIndex();
mStore.checkPlayer();
mPlayer->readRecord(reader, type);
if (getPlayerPtr().isInCell())
{
if (getPlayerPtr().getCell()->isExterior())
mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3(),
getPlayerPtr().getCell()->getCell()->getWorldSpace());
mWorldScene->preloadCell(*getPlayerPtr().getCell(), true);
}
break;
default:
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
&& !mWeatherManager->readRecord(reader, type) && !mWorldModel.readRecord(reader, type)
&& !mProjectileManager->readRecord(reader, type))
{
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
void World::ensureNeededRecords()
{
for (const auto& [id, value] : generateDefaultGameSettings())
{
if (mStore.get<ESM::GameSetting>().search(id) == nullptr)
{
ESM::GameSetting record;
record.mId = ESM::RefId::stringRefId(id);
record.mValue = value;
2022-07-05 17:19:47 +04:00
record.mRecordFlags = 0;
mStore.insertStatic(record);
}
}
for (const auto& [name, value] : generateDefaultGlobals())
{
if (mStore.get<ESM::Global>().search(ESM::RefId::stringRefId(name.getValue())) == nullptr)
{
ESM::Global record;
record.mId = ESM::RefId::stringRefId(name.getValue());
record.mValue = value;
2022-07-02 23:25:51 +04:00
record.mRecordFlags = 0;
mStore.insertStatic(record);
}
}
for (const auto& [id, model] : generateDefaultStatics())
{
if (mStore.get<ESM::Static>().search(ESM::RefId::stringRefId(id)) == nullptr)
{
ESM::Static record;
record.mId = ESM::RefId::stringRefId(id);
record.mModel = model;
2022-07-02 23:25:51 +04:00
record.mRecordFlags = 0;
mStore.insertStatic(record);
}
}
for (const auto& [id, model] : generateDefaultDoors())
{
if (mStore.get<ESM::Door>().search(ESM::RefId::stringRefId(id)) == nullptr)
{
ESM::Door record;
record.mId = ESM::RefId::stringRefId(id);
record.mModel = model;
2022-07-02 23:25:51 +04:00
record.mRecordFlags = 0;
mStore.insertStatic(record);
}
}
}
World::~World()
{
// Must be cleared before mRendering is destroyed
if (mProjectileManager)
mProjectileManager->clear();
}
2011-08-01 14:41:15 +02:00
void World::setRandomSeed(uint32_t seed)
{
mRandomSeed = seed;
}
void World::useDeathCamera()
{
2021-06-20 00:57:41 +02:00
mRendering->getCamera()->setMode(MWRender::Camera::Mode::ThirdPerson);
}
MWWorld::Player& World::getPlayer()
{
return *mPlayer;
}
const std::vector<int>& World::getESMVersions() const
{
return mESMVersions;
}
LocalScripts& World::getLocalScripts()
{
return mLocalScripts;
}
void World::setGlobalInt(GlobalVariableName name, int value)
{
bool dateUpdated = mTimeManager->updateGlobalInt(name, value);
if (dateUpdated)
updateSkyDate();
mGlobalVariables[name].setInteger(value);
}
void World::setGlobalFloat(GlobalVariableName name, float value)
2011-04-21 11:00:00 +02:00
{
bool dateUpdated = mTimeManager->updateGlobalFloat(name, value);
if (dateUpdated)
updateSkyDate();
mGlobalVariables[name].setFloat(value);
2011-04-21 11:00:00 +02:00
}
int World::getGlobalInt(GlobalVariableName name) const
{
return mGlobalVariables[name].getInteger();
}
float World::getGlobalFloat(GlobalVariableName name) const
{
return mGlobalVariables[name].getFloat();
}
char World::getGlobalVariableType(GlobalVariableName name) const
{
return mGlobalVariables.getType(name);
}
std::string_view World::getCellName(const MWWorld::CellStore* cell) const
{
if (!cell)
cell = mWorldScene->getCurrentCell();
return getCellName(*cell->getCell());
}
std::string_view World::getCellName(const MWWorld::Cell& cell) const
{
if (!cell.isExterior() || !cell.getDisplayName().empty())
return cell.getDisplayName();
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);
}
std::string_view World::getCellName(const ESM::Cell* cell) const
{
if (cell)
{
if (!cell->isExterior() || !cell->mName.empty())
2023-01-19 17:31:45 +01:00
return cell->mName;
if (const ESM::Region* region = mStore.get<ESM::Region>().search(cell->mRegion))
return region->mName;
}
return mStore.get<ESM::GameSetting>().find("sDefaultCellname")->mValue.getString();
}
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)
{
Ptr ret;
// the player is always in an active cell.
if (name == "Player")
{
return mPlayer->getPlayer();
}
2018-12-30 01:27:16 +03:00
for (CellStore* cellstore : mWorldScene->getActiveCells())
{
// TODO: caching still doesn't work efficiently here (only works for the one CellStore that the reference is
// in)
Ptr ptr = cellstore->getPtr(name);
if (!ptr.isEmpty())
return ptr;
}
if (!activeOnly)
{
2023-07-30 21:55:36 +02:00
ret = mWorldModel.getPtrByRefId(name);
if (!ret.isEmpty())
return ret;
}
if (searchInContainers)
{
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);
if (!ptr.isEmpty())
return 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
Ptr ptr = mPlayer->getPlayer().getClass().getContainerStore(mPlayer->getPlayer()).search(name);
return 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
Ptr World::getPtr(const ESM::RefId& name, bool activeOnly)
{
Ptr ret = searchPtr(name, activeOnly);
if (!ret.isEmpty())
return ret;
std::string error = "Failed to find an instance of object " + name.toDebugString();
if (activeOnly)
error += " in active cells";
throw std::runtime_error(error);
}
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);
}
2015-12-06 18:13:04 +01:00
struct FindContainerVisitor
{
ConstPtr mContainedPtr;
Ptr mResult;
FindContainerVisitor(const ConstPtr& containedPtr)
: mContainedPtr(containedPtr)
{
}
2021-06-23 23:13:59 +02:00
bool operator()(const Ptr& ptr)
{
if (mContainedPtr.getContainerStore() == &ptr.getClass().getContainerStore(ptr))
{
mResult = ptr;
return false;
}
return true;
}
};
Ptr World::findContainer(const ConstPtr& ptr)
{
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())
{
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);
2015-12-06 18:13:04 +01:00
if (!visitor.mResult.isEmpty())
return visitor.mResult;
}
return Ptr();
}
void World::addContainerScripts(const Ptr& reference, CellStore* cell)
{
if (reference.getType() == ESM::Container::sRecordId || reference.getType() == ESM::NPC::sRecordId
|| reference.getType() == ESM::Creature::sRecordId)
{
MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);
for (MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
{
const auto& script = it->getClass().getScript(*it);
2022-08-11 22:51:55 +02:00
if (!script.empty())
{
MWWorld::Ptr item = *it;
item.mCell = cell;
mLocalScripts.add(script, item);
}
}
}
}
void World::enable(const Ptr& reference)
2010-07-09 16:07:03 +02:00
{
if (!reference.isInCell())
return;
if (!reference.getRefData().isEnabled())
2010-07-09 16:07:03 +02:00
{
reference.getRefData().enable();
if (mWorldScene->getActiveCells().find(reference.getCell()) != mWorldScene->getActiveCells().end()
&& reference.getRefData().getCount())
mWorldScene->addObjectToScene(reference);
if (reference.getCellRef().getRefNum().hasContentFile())
{
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());
if (mRendering->pagingEnableObject(type, reference, true))
mWorldScene->reloadTerrain();
}
2010-07-09 16:07:03 +02:00
}
}
void World::removeContainerScripts(const Ptr& reference)
{
if (reference.getType() == ESM::Container::sRecordId || reference.getType() == ESM::NPC::sRecordId
|| reference.getType() == ESM::Creature::sRecordId)
{
MWWorld::ContainerStore& container = reference.getClass().getContainerStore(reference);
for (MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
{
const ESM::RefId& script = it->getClass().getScript(*it);
2022-08-11 22:51:55 +02:00
if (!script.empty())
{
MWWorld::Ptr item = *it;
mLocalScripts.remove(item);
}
}
2010-07-09 16:07:03 +02:00
}
}
void World::disable(const Ptr& reference)
2010-07-09 16:07:03 +02:00
{
if (!reference.getRefData().isEnabled())
return;
// disable is a no-op for items in containers
if (!reference.isInCell())
return;
if (reference == getPlayerPtr())
throw std::runtime_error("can not disable player object");
reference.getRefData().disable();
if (reference.getCellRef().getRefNum().hasContentFile())
{
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());
if (mRendering->pagingEnableObject(type, reference, false))
mWorldScene->reloadTerrain();
}
if (mWorldScene->getActiveCells().find(reference.getCell()) != mWorldScene->getActiveCells().end()
&& reference.getRefData().getCount())
{
mWorldScene->removeObjectFromScene(reference);
mWorldScene->addPostponedPhysicsObjects();
}
2010-07-09 16:07:03 +02:00
}
void World::advanceTime(double hours, bool incremental)
2010-07-18 18:29:16 +02: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 = mTimeManager->getGameTimeScale();
if (timeScaleFactor != 0.0f)
duration /= timeScaleFactor;
rechargeItems(duration, false);
}
mWeatherManager->advanceTime(hours, incremental);
mTimeManager->advanceTime(hours, mGlobalVariables);
updateSkyDate();
if (!incremental)
2017-07-28 16:50:52 +04:00
{
mRendering->notifyWorldSpaceChanged();
mProjectileManager->clear();
mDiscardMovements = true;
2017-07-28 16:50:52 +04:00
}
}
TimeStamp World::getTimeStamp() const
2012-09-18 20:53:32 +02:00
{
return mTimeManager->getTimeStamp();
}
bool World::toggleSky()
{
2015-06-16 20:56:48 +02:00
mSky = !mSky;
mRendering->setSkyEnabled(mSky);
return mSky;
}
int World::getMasserPhase() const
{
2015-06-16 20:56:48 +02:00
return mRendering->skyGetMasserPhase();
}
int World::getSecundaPhase() const
{
2015-06-16 20:56:48 +02:00
return mRendering->skyGetSecundaPhase();
}
void World::setMoonColour(bool red)
{
2015-06-16 20:56:48 +02:00
mRendering->skySetMoonColour(red);
}
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;
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;
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
2010-07-22 12:29:23 +02:00
}
void World::changeToCell(
const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{
const MWWorld::Cell* destinationCell = getWorldModel().getCell(cellId).getCell();
bool exteriorCell = destinationCell->isExterior();
mPhysics->clearQueuedMovement();
mDiscardMovements = true;
if (changeEvent && mCurrentWorldSpace != destinationCell->getNameId())
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
mCurrentWorldSpace = destinationCell->getNameId();
}
removeContainerScripts(getPlayerPtr());
if (exteriorCell)
mWorldScene->changeToExteriorCell(cellId, position, adjustPlayerPos, changeEvent);
else
mWorldScene->changeToInteriorCell(destinationCell->getNameId(), position, adjustPlayerPos, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
}
float World::getMaxActivationDistance() const
{
if (mActivationDistanceOverride >= 0)
return static_cast<float>(mActivationDistanceOverride);
static const int iMaxActivateDist
= mStore.get<ESM::GameSetting>().find("iMaxActivateDist")->mValue.getInteger();
return static_cast<float>(iMaxActivateDist);
}
MWWorld::Ptr World::getFacedObject()
{
MWWorld::Ptr facedObject;
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
facedObject = getFacedObject(getMaxActivationDistance() * 50, false);
else
{
float activationDistance = getActivationDistancePlusTelekinesis();
facedObject = getFacedObject(activationDistance, true);
if (!facedObject.isEmpty() && !facedObject.getClass().allowTelekinesis(facedObject)
&& mDistanceToFacedObject > getMaxActivationDistance()
&& !MWBase::Environment::get().getWindowManager()->isGuiMode())
2020-11-13 11:39:47 +04:00
return nullptr;
}
return facedObject;
}
2010-08-07 20:25:17 +02:00
float World::getDistanceToFacedObject()
{
return mDistanceToFacedObject;
}
osg::Matrixf World::getActorHeadTransform(const MWWorld::ConstPtr& actor) const
{
const MWRender::Animation* anim = mRendering->getAnimation(actor);
if (anim)
{
const osg::Node* node = anim->getNode("Head");
if (!node)
node = anim->getNode("Bip01 Head");
if (node)
2015-05-22 04:36:17 +02:00
{
osg::NodePathList nodepaths = node->getParentalNodePaths();
if (!nodepaths.empty())
return osg::computeLocalToWorld(nodepaths[0]);
2015-05-22 04:36:17 +02:00
}
}
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
}
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
{
if (ptr == getPlayerPtr())
throw std::runtime_error("can not delete player object");
ptr.getRefData().setCount(0);
2010-08-07 20:25:17 +02:00
if (ptr.isInCell()
&& mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()
&& ptr.getRefData().isEnabled())
{
mWorldScene->removeObjectFromScene(ptr);
mLocalScripts.remove(ptr);
removeContainerScripts(ptr);
}
2010-08-07 20:25:17 +02:00
}
}
void World::undeleteObject(const Ptr& ptr)
{
if (!ptr.getCellRef().hasContentFile())
return;
if (ptr.getRefData().isDeleted())
{
ptr.getRefData().setCount(1);
if (mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end()
&& ptr.getRefData().isEnabled())
{
mWorldScene->addObjectToScene(ptr);
const auto& script = ptr.getClass().getScript(ptr);
if (!script.empty())
mLocalScripts.add(script, ptr);
addContainerScripts(ptr, ptr.getCell());
}
}
}
MWWorld::Ptr World::moveObject(
const Ptr& ptr, CellStore* newCell, const osg::Vec3f& position, bool movePhysics, bool keepActive)
{
ESM::Position pos = ptr.getRefData().getPosition();
std::memcpy(pos.pos, &position, sizeof(osg::Vec3f));
ptr.getRefData().setPosition(pos);
CellStore* currCell = ptr.isInCell()
? ptr.getCell()
: nullptr; // currCell == nullptr should only happen for player, during initial startup
bool isPlayer = ptr == mPlayer->getPlayer();
2014-09-28 12:44:47 +02:00
bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell));
MWWorld::Ptr newPtr = ptr;
if (!isPlayer && !currCell)
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
+ " to another cell: current cell is nullptr");
if (!newCell)
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
+ " to another cell: new cell is nullptr");
if (currCell != newCell)
{
removeContainerScripts(ptr);
if (isPlayer)
{
if (!newCell->isExterior())
{
changeToInteriorCell(newCell->getCell()->getNameId(), pos, false);
removeContainerScripts(getPlayerPtr());
}
else
{
if (mWorldScene->isCellActive(*newCell))
mWorldScene->changePlayerCell(*newCell, pos, false);
else
mWorldScene->changeToExteriorCell(newCell->getCell()->getId(), pos, false);
}
addContainerScripts(getPlayerPtr(), newCell);
newPtr = getPlayerPtr();
}
else
{
2018-08-01 19:30:30 +04:00
bool currCellActive = mWorldScene->isCellActive(*currCell);
bool newCellActive = mWorldScene->isCellActive(*newCell);
if (!currCellActive && newCellActive)
{
newPtr = currCell->moveTo(ptr, newCell);
2021-09-27 22:23:00 +02:00
if (newPtr.getRefData().isEnabled())
mWorldScene->addObjectToScene(newPtr);
const auto& script = newPtr.getClass().getScript(newPtr);
if (!script.empty())
2018-07-08 23:13:24 +03:00
{
mLocalScripts.add(script, newPtr);
2018-07-08 23:13:24 +03:00
}
addContainerScripts(newPtr, newCell);
}
else if (!newCellActive && currCellActive)
{
mWorldScene->removeObjectFromScene(ptr, keepActive);
mLocalScripts.remove(ptr);
removeContainerScripts(ptr);
haveToMove = false;
2013-11-28 17:31:17 +01:00
newPtr = currCell->moveTo(ptr, newCell);
2020-11-13 11:39:47 +04:00
newPtr.getRefData().setBaseNode(nullptr);
}
else if (!currCellActive && !newCellActive)
newPtr = currCell->moveTo(ptr, newCell);
else // both cells active
{
newPtr = currCell->moveTo(ptr, newCell);
mRendering->updatePtr(ptr, newPtr);
MWBase::Environment::get().getSoundManager()->updatePtr(ptr, newPtr);
mPhysics->updatePtr(ptr, newPtr);
MWBase::MechanicsManager* mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(ptr, newPtr);
const auto& script = ptr.getClass().getScript(ptr);
if (!script.empty())
{
mLocalScripts.remove(ptr);
removeContainerScripts(ptr);
mLocalScripts.add(script, newPtr);
addContainerScripts(newPtr, newCell);
}
}
}
MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
}
if (haveToMove && newPtr.getRefData().getBaseNode())
{
mRendering->moveObject(newPtr, position);
if (movePhysics)
{
mPhysics->updatePosition(newPtr);
if (const MWPhysics::Object* object = mPhysics->getObject(newPtr))
updateNavigatorObject(*object);
}
2012-07-30 23:28:14 +04:00
}
if (isPlayer)
mWorldScene->playerMoved(position);
else
{
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
mWorldScene->removeFromPagedRefs(newPtr);
}
return newPtr;
}
MWWorld::Ptr World::moveObject(const Ptr& ptr, const osg::Vec3f& position, bool movePhysics, bool moveToActive)
{
CellStore* cell = ptr.getCell();
ESM::RefId worldspaceId
= cell->isExterior() ? cell->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
const ESM::ExteriorCellLocation index
= ESM::positionToExteriorCellLocation(position.x(), position.y(), worldspaceId);
CellStore* newCell = cell->isExterior() ? &mWorldModel.getExterior(index) : nullptr;
bool isCellActive = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior()
&& (newCell && mWorldScene->isCellActive(*newCell));
if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor()))
cell = newCell;
return moveObject(ptr, cell, position, movePhysics);
2011-08-02 18:50:31 +02:00
}
MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec, bool moveToActive)
{
auto* actor = mPhysics->getActor(ptr);
osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec;
if (actor)
actor->adjustPosition(vec);
if (ptr.getClass().isActor())
return moveObject(ptr, newpos, false, moveToActive && ptr != getPlayerPtr());
return moveObject(ptr, newpos);
}
void World::scaleObject(const Ptr& ptr, float scale, bool force)
{
if (!force && scale == ptr.getCellRef().getScale())
return;
if (mPhysics->getActor(ptr))
mNavigator->removeAgent(getPathfindingAgentBounds(ptr));
ptr.getCellRef().setScale(scale);
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
mWorldScene->removeFromPagedRefs(ptr);
2020-11-13 11:39:47 +04:00
if (ptr.getRefData().getBaseNode() != nullptr)
mWorldScene->updateObjectScale(ptr);
if (mPhysics->getActor(ptr))
{
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))
updateNavigatorObject(*object);
}
void World::rotateObject(const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags)
2012-05-29 10:15:29 +02:00
{
ESM::Position pos = ptr.getRefData().getPosition();
float* objRot = pos.rot;
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
{
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
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);
objRot[1] = Misc::normalizeAngle(objRot[1]);
objRot[2] = Misc::normalizeAngle(objRot[2]);
}
2013-04-28 01:14:58 -07:00
ptr.getRefData().setPosition(pos);
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
{
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))
updateNavigatorObject(*object);
2019-03-03 16:01:52 +03:00
}
2012-05-29 10:15:29 +02:00
}
void World::adjustPosition(const Ptr& ptr, bool force)
{
if (ptr.isEmpty())
{
Log(Debug::Warning) << "Unable to adjust position for empty object";
return;
}
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
2015-05-12 19:02:56 +02:00
if (!ptr.getRefData().getBaseNode())
{
// will be adjusted when Ptr's cell becomes active
return;
}
if (!ptr.isInCell())
{
Log(Debug::Warning) << "Unable to adjust position for object '" << ptr.getCellRef().getRefId()
<< "' - it has no cell";
return;
}
const float terrainHeight = ptr.getCell()->isExterior()
? getTerrainHeightAt(pos, ptr.getCell()->getCell()->getWorldSpace())
: -std::numeric_limits<float>::max();
pos.z() = std::max(pos.z(), terrainHeight)
+ 20; // place slightly above terrain. will snap down to ground with code below
// 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)))
{
osg::Vec3f traced
= mPhysics->traceDown(ptr, pos, ESM::getCellSize(ptr.getCell()->getCell()->getWorldSpace()));
pos.z() = std::min(pos.z(), traced.z());
}
moveObject(ptr, ptr.getCell(), pos);
}
2018-10-24 01:40:57 +03:00
void World::fixPosition()
{
2018-10-24 01:40:57 +03:00
const MWWorld::Ptr actor = getPlayerPtr();
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());
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
if (!mPhysics->castRay(pos, targetPos, MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door).mHit)
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, ESM::getCellSize(actor.getCell()->getCell()->getWorldSpace()));
if (traced != pos)
{
esmPos.pos[0] = traced.x();
esmPos.pos[1] = traced.y();
esmPos.pos[2] = traced.z();
ESM::RefId cell = actor.getCell()->getCell()->getId();
2023-01-19 17:31:45 +01:00
MWWorld::ActionTeleport(cell, esmPos, false).execute(actor);
}
}
2021-08-15 19:50:28 +02:00
void World::rotateWorldObject(const Ptr& ptr, const osg::Quat& rotate)
{
2020-11-13 11:39:47 +04:00
if (ptr.getRefData().getBaseNode() != nullptr)
{
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
mWorldScene->removeFromPagedRefs(ptr);
mRendering->rotateObject(ptr, rotate);
mPhysics->updateRotation(ptr, rotate);
if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(*object);
}
}
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
{
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 };
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;
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;
}
}
ipos.pos[0] = spawnPoint.x();
ipos.pos[1] = spawnPoint.y();
ipos.pos[2] = spawnPoint.z();
2016-02-29 17:16:33 +01:00
if (referenceObject.getClass().isActor())
{
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);
adjustPosition(placed, true); // snap to ground
2016-02-29 17:16:33 +01:00
return placed;
}
void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity, float duration)
{
mPhysics->queueObjectMovement(ptr, velocity, duration);
}
void World::updateAnimatedCollisionShape(const Ptr& ptr)
{
mPhysics->updateAnimatedCollisionShape(ptr);
}
void World::doPhysics(float duration, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
2011-01-29 17:39:34 +01:00
{
processDoors(duration);
2015-06-01 21:41:13 +02:00
mProjectileManager->update(duration);
mPhysics->stepSimulation(duration, mDiscardMovements, frameStart, frameNumber, stats);
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;
mPhysics->moveActors();
2018-08-25 23:51:54 +03:00
}
void World::updateNavigator()
{
auto navigatorUpdateGuard = mNavigator->makeUpdateGuard();
mPhysics->forEachAnimatedObject([&](const auto& pair) {
const auto [object, changed] = pair;
if (changed)
updateNavigatorObject(*object, navigatorUpdateGuard.get());
});
2018-08-25 23:51:54 +03:00
for (const auto& door : mDoorStates)
if (const auto object = mPhysics->getObject(door.first))
updateNavigatorObject(*object, navigatorUpdateGuard.get());
2018-08-25 23:51:54 +03:00
mNavigator->update(getPlayerPtr().getRefData().getPosition().asVec3(), navigatorUpdateGuard.get());
}
void World::updateNavigatorObject(
const MWPhysics::Object& object, const DetourNavigator::UpdateGuard* navigatorUpdateGuard)
2018-08-25 23:51:54 +03: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() });
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)
{
const ESM::Position& objPos = door.getRefData().getPosition();
auto oldRot = objPos.asRotationVec3();
auto newRot = oldRot;
float minRot = door.getCellRef().getPosition().rot[2];
float maxRot = minRot + osg::DegreesToRadians(90.f);
float diff = duration * osg::DegreesToRadians(90.f) * (state == MWWorld::DoorState::Opening ? 1 : -1);
float targetRot = std::clamp(oldRot.z() + diff, minRot, maxRot);
newRot.z() = targetRot;
rotateObject(door, newRot, MWBase::RotationFlag_none);
2019-08-25 15:20:14 +02:00
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
/// \todo should use convexSweepTest here
bool collisionWithActor = false;
for (auto& [ptr, point, normal] :
mPhysics->getCollisionsPoints(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor))
{
if (ptr.getClass().isActor())
{
auto localPoint = objPos.asVec3() - point;
osg::Vec3f direction = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * localPoint - localPoint;
direction.normalize();
mPhysics->reportCollision(Misc::Convert::toBullet(point), Misc::Convert::toBullet(normal));
if (direction * normal < 0) // door is turning away from actor
continue;
collisionWithActor = true;
// Collided with actor, ask actor to try to avoid door
if (ptr != getPlayerPtr())
{
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) // Only add it once
seq.stack(MWMechanics::AiAvoidDoor(door), ptr);
}
// we need to undo the rotation
reached = false;
}
}
// 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;
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;
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
rotateObject(door, oldRot, MWBase::RotationFlag_none);
}
return reached;
}
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
{
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
{
// 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.
mDoorStates.erase(it++);
}
2013-04-28 14:59:15 +02:00
else
{
bool reached = rotateDoor(it->first, it->second, duration);
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++);
}
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
}
void World::setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external)
{
MWPhysics::Actor* physicActor = mPhysics->getActor(ptr);
if (physicActor && physicActor->getCollisionMode() != internal)
{
physicActor->enableCollisionMode(internal);
physicActor->enableCollisionBody(external);
}
}
bool World::isActorCollisionEnabled(const MWWorld::Ptr& ptr)
{
MWPhysics::Actor* physicActor = mPhysics->getActor(ptr);
return physicActor && physicActor->getCollisionMode();
}
bool World::toggleCollisionMode()
{
if (mPhysics->toggleCollisionMode())
{
adjustPosition(getPlayerPtr(), true);
return true;
}
return false;
}
2015-05-02 22:45:27 +02:00
bool World::toggleRenderMode(MWRender::RenderMode mode)
{
2015-05-03 00:39:01 +02:00
switch (mode)
{
case MWRender::Render_CollisionDebug:
return mPhysics->toggleDebugRendering();
default:
return mRendering->toggleRenderMode(mode);
}
}
void World::update(float duration, bool paused)
{
2014-01-11 06:47:58 +01:00
if (mGoToJail && !paused)
goToJail();
// Reset "traveling" flag - there was a frame to detect traveling.
mPlayerTraveling = false;
// 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
updateWeather(duration, paused);
updateNavigator();
mPlayer->update();
mPhysics->debugDraw();
mWorldScene->update(duration);
mRendering->update(duration, paused);
updateSoundListener();
2015-05-26 16:40:44 +02:00
mSpellPreloadTimer -= duration;
if (mSpellPreloadTimer <= 0.f)
{
mSpellPreloadTimer = 0.1f;
preloadSpells();
}
if (mWorldScene->hasCellLoaded())
{
mNavigator->wait(DetourNavigator::WaitConditionType::requiredTilesPresent,
MWBase::Environment::get().getWindowManager()->getLoadingScreen());
mWorldScene->resetCellLoaded();
}
}
void World::updatePhysics(
float duration, bool paused, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{
if (!paused)
{
doPhysics(duration, frameStart, frameNumber, stats);
}
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
}
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();
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);
if (!enchantId.empty())
{
2020-07-08 14:12:55 +04:00
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
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);
if (!enchantId.empty())
{
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
if (ench && ench->mData.mType == ESM::Enchantment::WhenStrikes)
preloadEffects(&ench->mEffects);
}
}
}
void World::updateSoundListener()
{
2021-06-20 00:57:41 +02:00
osg::Vec3f cameraPosition = mRendering->getCamera()->getPosition();
2023-11-16 18:53:33 +04:00
const auto& player = getPlayerPtr();
const ESM::Position& refpos = player.getRefData().getPosition();
osg::Vec3f listenerPos;
2015-05-12 19:02:56 +02:00
if (isFirstPerson())
2021-06-20 00:57:41 +02:00
listenerPos = cameraPosition;
else
2023-11-16 18:53:33 +04:00
listenerPos = refpos.asVec3() + osg::Vec3f(0, 0, 1.85f * mPhysics->getHalfExtents(player).z());
2015-05-12 19:02:56 +02: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));
osg::Vec3f forward = listenerOrient * osg::Vec3f(0, 1, 0);
osg::Vec3f up = listenerOrient * osg::Vec3f(0, 0, 1);
2023-11-16 18:53:33 +04:00
bool underwater = isUnderwater(player.getCell(), cameraPosition);
2015-05-12 19:02:56 +02:00
MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater);
}
void World::updateWindowManager()
{
try
{
// inform the GUI about focused object
MWWorld::Ptr object = getFacedObject();
// retrieve object dimensions so we know where to place the floating label
if (!object.isEmpty())
{
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);
MWBase::Environment::get().getWindowManager()->setFocusObjectScreenCoords(
screenBounds.x(), screenBounds.y(), screenBounds.z(), screenBounds.w());
}
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();
}
}
2012-03-25 20:52:56 +02:00
MWWorld::Ptr World::getFacedObject(float maxDistance, bool ignorePlayer)
{
const float camDist = mRendering->getCamera()->getCameraDistance();
maxDistance += camDist;
MWWorld::Ptr facedObject;
2016-07-06 22:09:39 +09:00
MWRender::RenderingManager::RayResult rayToObject;
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);
}
else
2016-07-06 22:09:39 +09:00
rayToObject = mRendering->castCameraToViewportRay(0.5f, 0.5f, maxDistance, ignorePlayer);
facedObject = rayToObject.mHitObject;
if (facedObject.isEmpty() && rayToObject.mHitRefnum.isSet())
facedObject = MWBase::Environment::get().getWorldModel()->getPtr(rayToObject.mHitRefnum);
if (rayToObject.mHit)
mDistanceToFacedObject = (rayToObject.mRatio * maxDistance) - camDist;
2016-07-07 21:46:58 +09:00
else
mDistanceToFacedObject = -1;
2016-07-06 22:09:39 +09:00
return facedObject;
}
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())
res.mHitObject = MWBase::Environment::get().getWorldModel()->getPtr(rayRes.mHitRefnum);
2022-03-18 00:27:16 +01:00
return res.mHit;
}
bool World::isCellExterior() const
{
const CellStore* currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
return currentCell->getCell()->isExterior();
}
return false;
}
bool World::isCellQuasiExterior() const
{
const CellStore* currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
return currentCell->getCell()->isQuasiExterior();
}
return false;
}
2023-05-09 22:08:17 +02:00
ESM::RefId World::getCurrentWorldspace() const
{
const CellStore* cellStore = mWorldScene->getCurrentCell();
if (cellStore)
2023-05-09 22:08:17 +02:00
return cellStore->getCell()->getWorldSpace();
return ESM::Cell::sDefaultWorldspaceId;
}
int World::getCurrentWeather() const
{
return mWeatherManager->getWeatherID();
}
2022-05-13 18:58:00 -07:00
int World::getNextWeather() const
{
return mWeatherManager->getNextWeatherID();
}
float World::getWeatherTransition() const
{
return mWeatherManager->getTransitionFactor();
}
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)
{
mWeatherManager->changeWeather(region, id);
}
void World::modRegion(const ESM::RefId& regionid, const std::vector<uint8_t>& chances)
{
mWeatherManager->modRegion(regionid, chances);
}
struct GetDoorMarkerVisitor
2012-08-28 17:30:34 +02: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;
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);
newMarker.dest = ref.mRef.getDestCell();
2012-08-28 17:30:34 +02:00
ESM::Position pos = ref.mData.getPosition();
newMarker.x = pos.pos[0];
newMarker.y = pos.pos[1];
mOut.push_back(newMarker);
2012-08-28 17:30:34 +02:00
}
return true;
2012-08-28 17:30:34 +02:00
}
};
void World::getDoorMarkers(CellStore& cell, std::vector<World::DoorMarker>& out)
{
GetDoorMarkerVisitor visitor{ out };
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
}
bool World::toggleWater()
{
2015-06-02 16:35:35 +02:00
return mRendering->toggleRenderMode(MWRender::Render_Water);
}
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();
}
void World::PCDropped(const Ptr& item)
{
const auto& script = item.getClass().getScript(item);
// 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())
item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1);
}
MWWorld::Ptr World::placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY, int amount, bool copy)
2012-05-14 17:41:17 +02:00
{
2015-06-01 01:57:15 +02:00
const float maxDist = 200.f;
MWRender::RenderingManager::RayResult result
= mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
CellStore* cell = getPlayerPtr().getCell();
ESM::Position pos = getPlayerPtr().getRefData().getPosition();
2015-06-01 01:57:15 +02:00
if (result.mHit)
{
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
}
// We want only the Z part of the player's rotation
pos.rot[0] = 0;
pos.rot[1] = 0;
// copy the object and set its count
Ptr dropped
= copy ? copyObjectToCell(object, cell, pos, amount, true) : moveObjectToCell(object, cell, pos, true);
// only the player place items in the world, so no need to check actor
PCDropped(dropped);
return dropped;
2012-05-14 17:41:17 +02:00
}
bool World::canPlaceObject(float cursorX, float cursorY)
{
2015-06-01 01:57:15 +02:00
const float maxDist = 200.f;
MWRender::RenderingManager::RayResult result
= mRendering->castCameraToViewportRay(cursorX, cursorY, maxDist, true, true);
2015-06-01 01:57:15 +02:00
if (result.mHit)
{
// 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))
return false;
return true;
}
else
return false;
}
Ptr World::copyObjectToCell(const ConstPtr& object, CellStore* cell, ESM::Position pos, int count, bool adjustPos)
{
if (!cell)
throw std::runtime_error("copyObjectToCell(): cannot copy object to null cell");
if (cell->isExterior())
{
const ESM::ExteriorCellLocation index
= ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace());
cell = &mWorldModel.getExterior(index);
}
MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count);
2023-07-17 17:06:28 +02:00
initObjectInCell(dropped, *cell, adjustPos);
return dropped;
}
Ptr World::moveObjectToCell(const Ptr& object, CellStore* cell, ESM::Position pos, bool adjustPos)
{
if (!cell)
throw std::runtime_error("moveObjectToCell(): cannot move object to null cell");
if (cell->isExterior())
{
const ESM::ExteriorCellLocation index
= ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace());
cell = &mWorldModel.getExterior(index);
}
MWWorld::Ptr dropped = object.getClass().moveToCell(object, *cell, pos);
2023-07-17 17:06:28 +02:00
initObjectInCell(dropped, *cell, adjustPos);
return dropped;
}
2023-07-17 17:06:28 +02:00
void World::initObjectInCell(const Ptr& object, CellStore& cell, bool adjustPos)
{
2023-07-17 17:06:28 +02:00
if (mWorldScene->isCellActive(cell))
{
if (object.getRefData().isEnabled())
2012-07-26 19:06:48 +04:00
{
mWorldScene->addObjectToScene(object);
2012-07-26 19:06:48 +04:00
}
const auto& script = object.getClass().getScript(object);
if (!script.empty())
{
mLocalScripts.add(script, object);
}
2023-07-17 17:06:28 +02:00
addContainerScripts(object, &cell);
}
if (!object.getClass().isActor() && adjustPos && object.getRefData().getBaseNode())
2015-05-22 01:43:45 +02:00
{
// Adjust position so the location we wanted ends up in the middle of the object bounding box
osg::ComputeBoundsVisitor computeBounds;
computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem);
object.getRefData().getBaseNode()->accept(computeBounds);
2015-05-22 01:43:45 +02:00
osg::BoundingBox bounds = computeBounds.getBoundingBox();
if (bounds.valid())
{
2023-07-17 17:06:28 +02:00
ESM::Position pos = object.getRefData().getPosition();
2015-05-22 01:43:45 +02:00
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();
moveObject(object, pos.asVec3());
2015-05-22 01:43:45 +02:00
}
}
}
MWWorld::Ptr World::dropObjectOnGround(const Ptr& actor, const Ptr& object, int amount, bool copy)
{
MWWorld::CellStore* cell = actor.getCell();
2013-01-09 21:16:45 +00:00
ESM::Position pos = actor.getRefData().getPosition();
// We want only the Z part of the actor's rotation
pos.rot[0] = 0;
pos.rot[1] = 0;
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
float len = 1000000.0;
2012-07-25 20:25:53 +04:00
MWRender::RenderingManager::RayResult result = mRendering->castRay(orig, orig + dir * len, true, true);
2015-06-01 01:57:15 +02:00
if (result.mHit)
pos.pos[2] = result.mHitPointWorld.z();
2012-07-25 20:25:53 +04:00
// copy the object and set its count
Ptr dropped
= copy ? copyObjectToCell(object, cell, pos, amount, true) : moveObjectToCell(object, cell, pos, true);
if (actor == mPlayer->getPlayer()) // Only call if dropped by player
PCDropped(dropped);
return dropped;
}
2012-05-23 01:32:36 +02:00
void World::processChangedSettings(const Settings::CategorySettingVector& settings)
{
mRendering->processChangedSettings(settings);
2012-05-23 01:32:36 +02:00
}
bool World::isFlying(const MWWorld::Ptr& ptr) const
{
if (!ptr.getClass().isActor())
return false;
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
if (stats.isDead())
return false;
2020-12-22 06:13:35 +03:00
const bool isPlayer = ptr == getPlayerConstPtr();
2023-05-23 19:06:08 +02:00
if (!(isPlayer && mGodMode)
&& stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Paralyze).getModifier() > 0)
2020-12-22 06:13:35 +03:00
return false;
if (ptr.getClass().canFly(ptr))
2020-12-22 06:13:35 +03:00
return true;
2023-05-23 19:06:08 +02:00
if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Levitate).getMagnitude() > 0
&& isLevitationEnabled())
return true;
2015-05-12 19:02:56 +02:00
const MWPhysics::Actor* actor = mPhysics->getActor(ptr);
if (!actor)
2015-05-12 19:02:56 +02:00
return true;
return false;
}
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);
2023-05-23 19:06:08 +02:00
if (stats.getMagicEffects().getOrDefault(ESM::MagicEffect::SlowFall).getMagnitude() > 0)
2013-10-02 22:53:29 +02:00
return true;
return false;
}
bool World::isSubmerged(const MWWorld::ConstPtr& object) const
{
return isUnderwater(object, 1.0f / mSwimHeightScale);
}
bool World::isSwimming(const MWWorld::ConstPtr& object) const
{
return isUnderwater(object, mSwimHeightScale);
}
bool World::isWading(const MWWorld::ConstPtr& object) const
{
const float kneeDeep = 0.25f;
return isUnderwater(object, kneeDeep);
}
bool World::isUnderwater(const MWWorld::ConstPtr& object, const float heightRatio) const
{
2015-06-01 21:41:13 +02:00
osg::Vec3f pos(object.getRefData().getPosition().asVec3());
pos.z() += heightRatio * 2 * mPhysics->getRenderingHalfExtents(object).z();
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
return isUnderwater(currCell, pos);
}
2015-06-01 21:41:13 +02:00
bool World::isUnderwater(const MWWorld::CellStore* cell, const osg::Vec3f& pos) const
{
if (!cell)
return false;
if (!(cell->getCell()->hasWater()))
{
return false;
}
2015-06-01 21:41:13 +02:00
return pos.z() < cell->getWaterLevel();
}
2012-08-14 20:33:29 +04:00
bool World::isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr& target) const
{
const MWWorld::CellStore* cell = target.getCell();
if (!cell->getCell()->hasWater())
return true;
float waterlevel = cell->getWaterLevel();
2018-05-26 17:44:25 +03:00
// SwimHeightScale affects the upper z position an actor can swim to
// 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;
}
bool World::isOnGround(const MWWorld::Ptr& ptr) const
{
2015-06-01 02:40:42 +02:00
return mPhysics->isOnGround(ptr);
}
void World::togglePOV(bool force)
2015-05-21 23:54:39 +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)
{
return mRendering->getCamera()->toggleVanityMode(enable);
2015-05-21 23:54:39 +02:00
}
2020-06-27 00:58:33 +02:00
void World::disableDeferredPreviewRotation()
{
mRendering->getCamera()->disableDeferredPreviewRotation();
2020-06-27 00:58:33 +02:00
}
void World::applyDeferredPreviewRotationToPlayer(float dt)
{
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
bool World::vanityRotateCamera(const float* rot)
{
2021-06-20 00:57:41 +02:00
auto* camera = mRendering->getCamera();
if (!camera->isVanityOrPreviewModeEnabled())
return false;
2021-06-20 00:57:41 +02:00
camera->setPitch(camera->getPitch() + rot[0]);
camera->setYaw(camera->getYaw() + rot[2]);
return true;
}
void World::saveLoaded()
{
mStore.rebuildIdsIndex();
mStore.validateDynamic();
mTimeManager->setup(mGlobalVariables);
}
2013-04-29 17:56:16 +02:00
void World::setupPlayer()
{
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
{
// Remove the old CharacterController
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
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());
MWBase::Environment::get().getLuaManager()->objectRemovedFromScene(getPlayerPtr());
2013-05-15 17:54:18 +02:00
mPlayer->set(player);
}
2015-05-01 18:21:50 +02:00
Ptr ptr = mPlayer->getPlayer();
mRendering->setupPlayer(ptr);
MWBase::Environment::get().getLuaManager()->setupPlayer(ptr);
}
2012-08-14 20:33:29 +04:00
void World::renderPlayer()
{
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr(), true);
2015-06-01 00:50:48 +02:00
MWWorld::Ptr player = getPlayerPtr();
mRendering->renderPlayer(player);
MWRender::NpcAnimation* anim = static_cast<MWRender::NpcAnimation*>(mRendering->getAnimation(player));
player.getClass().getInventoryStore(player).setInvListener(anim);
player.getClass().getInventoryStore(player).setContListener(anim);
2015-05-01 18:21:50 +02:00
scaleObject(player, player.getCellRef().getScale(), true); // apply race height
rotateObject(player, osg::Vec3f(), MWBase::RotationFlag_inverseOrder | MWBase::RotationFlag_adjust);
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());
2015-05-28 02:45:38 +02:00
mPhysics->remove(getPlayerPtr());
mPhysics->addActor(getPlayerPtr(), getPlayerPtr().getClass().getModel(getPlayerPtr()));
applyLoopingParticles(player);
2018-04-22 19:22:00 +03: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
{
CellStore* currentCell = mWorldScene->getCurrentCell();
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());
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;
if (isUnderwater(currentCell, playerPos) || isWalkingOnWater(player))
return Rest_PlayerIsUnderwater;
2015-05-12 19:02:56 +02:00
float fallHeight = player.getClass().getCreatureStats(player).getFallHeight();
float epsilon = 1e-4;
if ((actor->getCollisionMode() && (!mPhysics->isOnSolidGround(player) || fallHeight >= epsilon))
|| isFlying(player))
return Rest_PlayerIsInAir;
if (currentCell->getCell()->noSleep() || player.getClass().getNpcStats(player).isWerewolf())
return Rest_OnlyWaiting;
return Rest_Allowed;
2012-09-18 20:53:32 +02:00
}
2012-09-25 02:35:50 +02:00
MWRender::Animation* World::getAnimation(const MWWorld::Ptr& ptr)
{
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;
}
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
{
mRendering->screenshot(image, w, h);
2014-01-24 17:49:16 +01:00
}
2018-05-26 17:44:25 +03:00
bool World::screenshot360(osg::Image* image)
2017-11-07 13:07:11 +01: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);
switch (state)
2013-04-28 14:59:15 +02:00
{
2019-08-25 15:20:14 +02:00
case MWWorld::DoorState::Idle:
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
break;
2019-08-25 15:20:14 +02:00
case MWWorld::DoorState::Closing:
state = MWWorld::DoorState::Opening; // if closing, then open
break;
2019-08-25 15:20:14 +02:00
case MWWorld::DoorState::Opening:
default:
2019-08-25 15:20:14 +02:00
state = MWWorld::DoorState::Closing; // if opening, then close
break;
2013-04-28 14:59:15 +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)
{
door.getClass().setDoorState(door, state);
mDoorStates[door] = state;
2019-08-25 15:20:14 +02:00
if (state == MWWorld::DoorState::Idle)
{
mDoorStates.erase(door);
rotateDoor(door, state, 1);
}
}
bool World::getPlayerStandingOn(const MWWorld::ConstPtr& object)
{
2015-05-30 01:32:00 +02:00
MWWorld::Ptr player = getPlayerPtr();
return mPhysics->isActorStandingOn(player, object);
}
bool World::getActorStandingOn(const MWWorld::ConstPtr& object)
{
2015-05-30 01:32:00 +02:00
std::vector<MWWorld::Ptr> actors;
mPhysics->getActorsStandingOn(object, actors);
return !actors.empty();
}
void World::getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr>& actors)
{
mPhysics->getActorsStandingOn(object, actors);
}
bool World::getPlayerCollidingWith(const MWWorld::ConstPtr& object)
{
2015-05-30 01:32:00 +02:00
MWWorld::Ptr player = getPlayerPtr();
return mPhysics->isActorCollidingWith(player, object);
}
bool World::getActorCollidingWith(const MWWorld::ConstPtr& object)
{
2015-05-30 01:32:00 +02:00
std::vector<MWWorld::Ptr> actors;
mPhysics->getActorsCollidingWith(object, actors);
return !actors.empty();
}
void World::hurtStandingActors(const ConstPtr& object, float healthPerSecond)
{
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)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
if (stats.isDead())
continue;
2017-03-25 22:40:11 +04:00
mPhysics->markAsNonSolid(object);
if (actor == getPlayerPtr() && mGodMode)
2018-12-30 01:27:16 +03:00
continue;
2017-03-25 22:40:11 +04:00
MWMechanics::DynamicStat<float> health = stats.getHealth();
health.setCurrent(health.getCurrent() - healthPerSecond * MWBase::Environment::get().getFrameDuration());
stats.setHealth(health);
if (healthPerSecond > 0.0f)
{
if (actor == getPlayerPtr())
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
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);
}
}
}
void World::hurtCollidingActors(const ConstPtr& object, float healthPerSecond)
{
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)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
if (stats.isDead())
continue;
2017-03-25 22:40:11 +04:00
mPhysics->markAsNonSolid(object);
if (actor == getPlayerPtr() && mGodMode)
continue;
2017-03-25 22:40:11 +04:00
MWMechanics::DynamicStat<float> health = stats.getHealth();
health.setCurrent(health.getCurrent() - healthPerSecond * MWBase::Environment::get().getFrameDuration());
stats.setHealth(health);
if (healthPerSecond > 0.0f)
{
if (actor == getPlayerPtr())
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
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);
}
}
}
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
bool World::isInStorm() const
{
if (isCellExterior() || isCellQuasiExterior())
2015-04-25 15:19:17 +02:00
return mWeatherManager->isInStorm();
else
return false;
}
osg::Vec3f World::getStormDirection() const
{
if (isCellExterior() || isCellQuasiExterior())
2015-04-25 15:19:17 +02:00
return mWeatherManager->getStormDirection();
else
return osg::Vec3f(0, 1, 0);
}
struct GetContainersOwnedByVisitor
{
GetContainersOwnedByVisitor(const MWWorld::ConstPtr& owner, std::vector<MWWorld::Ptr>& out)
: mOwner(owner)
, mOut(out)
{
}
MWWorld::ConstPtr mOwner;
std::vector<MWWorld::Ptr>& mOut;
bool operator()(const MWWorld::Ptr& ptr)
{
if (ptr.getRefData().isDeleted())
return true;
// vanilla Morrowind does not allow to sell items from containers with zero capacity
if (ptr.getClass().getCapacity(ptr) <= 0.f)
return true;
if (ptr.getCellRef().getOwner() == mOwner.getCellRef().getRefId())
mOut.push_back(ptr);
return true;
}
};
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
{
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
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
{
cellstore->forEach([&](const auto& ptr) {
if (ptr.getRefData().getBaseNode() && ptr.getCellRef().getOwner() == npc.getCellRef().getRefId())
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)
{
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())
return false; // not in active cell
2015-06-01 01:57:15 +02:00
return mPhysics->getLineOfSight(actor, targetActor);
}
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
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);
if (physicActor)
physicActor->enableCollisionBody(enable);
2013-06-27 14:11:20 -07:00
}
static std::optional<ESM::Position> searchMarkerPosition(const CellStore& cellStore, std::string_view editorId)
{
for (const MWWorld::LiveCellRef<ESM4::Static>& stat4 : cellStore.getReadOnlyEsm4Statics().mList)
{
if (Misc::StringUtils::lowerCase(stat4.mBase->mEditorId) == editorId)
return stat4.mRef.getPosition();
}
return std::nullopt;
}
static std::optional<ESM::Position> searchDoorDestInCell(const CellStore& cellStore)
{
ESM::RefId cellId = cellStore.getCell()->getId();
std::vector<const MWWorld::CellRef*> sortedDoors;
for (const MWWorld::LiveCellRef<ESM::Door>& door : cellStore.getReadOnlyDoors().mList)
{
2018-12-30 01:27:16 +03:00
if (!door.mRef.getTeleport())
continue;
sortedDoors.push_back(&door.mRef);
}
for (const MWWorld::LiveCellRef<ESM4::Door>& door : cellStore.getReadOnlyEsm4Doors().mList)
{
if (!door.mRef.getTeleport())
continue;
2018-12-30 01:27:16 +03:00
sortedDoors.push_back(&door.mRef);
}
// 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();
});
WorldModel* worldModel = MWBase::Environment::get().getWorldModel();
2018-12-30 01:27:16 +03:00
for (const MWWorld::CellRef* door : sortedDoors)
{
const MWWorld::CellStore& source = worldModel->getCell(door->getDestCell());
// Find door leading to our current teleport door
// and use its destination to position inside cell.
// \note Using _any_ door pointed to the cell,
// not the one pointed to current door.
for (const MWWorld::LiveCellRef<ESM::Door>& destDoor : source.getReadOnlyDoors().mList)
{
if (cellId == destDoor.mRef.getDestCell())
{
ESM::Position doorDest = destDoor.mRef.getDoorDest();
doorDest.rot[0] = doorDest.rot[1] = doorDest.rot[2] = 0;
return doorDest;
}
}
for (const MWWorld::LiveCellRef<ESM4::Door>& destDoor : source.getReadOnlyEsm4Doors().mList)
{
if (cellId == destDoor.mRef.getDestCell())
return destDoor.mRef.getDoorDest();
}
}
return std::nullopt;
}
ESM::RefId World::findInteriorPosition(std::string_view name, ESM::Position& pos)
{
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
2023-06-08 22:45:37 +02:00
const MWWorld::CellStore* cellStore = mWorldModel.findInterior(name);
if (!cellStore)
return ESM::RefId();
ESM::RefId cellId = cellStore->getCell()->getId();
2023-06-08 22:45:37 +02:00
if (std::optional<ESM::Position> destPos = searchMarkerPosition(*cellStore, "cocmarkerheading"))
{
pos = *destPos;
return cellId;
}
2023-06-08 22:45:37 +02:00
if (std::optional<ESM::Position> destPos = searchDoorDestInCell(*cellStore))
{
pos = *destPos;
return cellId;
}
2023-06-08 22:45:37 +02:00
if (std::optional<ESM::Position> destPos = searchMarkerPosition(*cellStore, "xmarkerheading"))
{
pos = *destPos;
return cellId;
}
// Fall back to the first static location.
2023-06-08 22:45:37 +02:00
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 cellId;
}
2023-06-08 22:45:37 +02:00
const MWWorld::CellRefList<ESM::Static>::List& statics = cellStore->getReadOnlyStatics().mList;
2018-12-30 01:27:16 +03:00
if (!statics.empty())
{
pos = statics.begin()->mRef.getPosition();
2019-06-30 16:03:24 +03:00
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
return cellId;
}
return ESM::RefId();
}
ESM::RefId World::findExteriorPosition(std::string_view nameId, ESM::Position& pos)
{
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
const MWWorld::CellStore* cellStore = mWorldModel.findCell(nameId);
if (cellStore != nullptr && !cellStore->isExterior())
return ESM::RefId();
if (!cellStore)
2022-08-27 13:07:59 +02:00
{
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)
{
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{})
cellStore
= &mWorldModel.getExterior(ESM::ExteriorCellLocation(x, y, ESM::Cell::sDefaultWorldspaceId));
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
}
}
if (!cellStore)
return ESM::RefId();
const MWWorld::Cell* ext = cellStore->getCell();
if (std::optional<ESM::Position> destPos = searchMarkerPosition(*cellStore, "cocmarkerheading"))
{
pos = *destPos;
return ext->getId();
}
if (std::optional<ESM::Position> destPos = searchMarkerPosition(*cellStore, "xmarkerheading"))
{
pos = *destPos;
return ext->getId();
}
int x = ext->getGridX();
int y = ext->getGridY();
osg::Vec2 posFromIndex = indexToPosition(ESM::ExteriorCellLocation(x, y, ext->getWorldSpace()), true);
pos.pos[0] = posFromIndex.x();
pos.pos[1] = posFromIndex.y();
// Note: Z pos will be adjusted by adjustPosition later
pos.pos[2] = 0;
return ext->getId();
}
void World::enableTeleporting(bool enable)
{
mTeleportEnabled = enable;
}
bool World::isTeleportingEnabled() const
{
return mTeleportEnabled;
}
void World::enableLevitation(bool enable)
{
mLevitationEnabled = enable;
}
bool World::isLevitationEnabled() const
{
return mLevitationEnabled;
}
void World::reattachPlayerCamera()
{
2015-05-21 23:54:39 +02:00
mRendering->rebuildPtr(getPlayerPtr());
}
2020-12-22 06:19:18 +03:00
bool World::getGodModeState() const
{
return mGodMode;
}
bool World::toggleGodMode()
{
2013-09-15 15:48:32 -04:00
mGodMode = !mGodMode;
2013-08-29 20:25:36 -04:00
return mGodMode;
}
2015-02-10 20:25:57 +01:00
bool World::toggleScripts()
{
mScriptsEnabled = !mScriptsEnabled;
return mScriptsEnabled;
}
bool World::getScriptsEnabled() const
{
return mScriptsEnabled;
}
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
{
GameContentLoader gameContentLoader;
EsmLoader esmLoader(mStore, mReaders, encoder, mESMVersions);
gameContentLoader.addLoader(".esm", esmLoader);
gameContentLoader.addLoader(".esp", esmLoader);
gameContentLoader.addLoader(".omwgame", esmLoader);
gameContentLoader.addLoader(".omwaddon", esmLoader);
gameContentLoader.addLoader(".project", esmLoader);
OMWScriptsLoader omwScriptsLoader(mStore);
gameContentLoader.addLoader(".omwscripts", omwScriptsLoader);
2018-12-30 01:27:16 +03:00
int idx = 0;
for (const std::string& file : content)
{
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))
{
gameContentLoader.load(col.getPath(file), idx, listener);
}
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);
}
2018-12-30 01:27:16 +03:00
idx++;
}
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.
}
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)
{
if (!Settings::groundcover().mEnabled)
return;
Log(Debug::Info) << "Loading groundcover:";
2022-06-03 18:59:08 +02:00
mGroundcoverStore.init(mStore.get<ESM::Static>(), fileCollections, groundcoverFiles, encoder, listener);
}
MWWorld::SpellCastState World::startSpellCast(const Ptr& actor)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
std::string_view message;
MWWorld::SpellCastState result = MWWorld::SpellCastState::Success;
bool isPlayer = (actor == getPlayerPtr());
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();
if (!selectedSpell.empty())
{
const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);
int spellCost = MWMechanics::calcSpellCost(*spell);
// Check mana
bool godmode = (isPlayer && mGodMode);
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
if (spellCost > 0 && magicka.getCurrent() < spellCost && !godmode)
{
message = "#{sMagicInsufficientSP}";
result = MWWorld::SpellCastState::InsufficientMagicka;
}
// If this is a power, check if it was already used in the last 24h
if (result == MWWorld::SpellCastState::Success && spell->mData.mType == ESM::Spell::ST_Power
&& !stats.getSpells().canUsePower(spell))
{
message = "#{sPowerAlreadyUsed}";
result = MWWorld::SpellCastState::PowerAlreadyUsed;
}
if (result == MWWorld::SpellCastState::Success && !godmode)
{
// Reduce mana
magicka.setCurrent(magicka.getCurrent() - spellCost);
stats.setMagicka(magicka);
// 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);
}
}
if (isPlayer && result != MWWorld::SpellCastState::Success)
MWBase::Environment::get().getWindowManager()->messageBox(message);
return result;
}
void World::castSpell(const Ptr& actor, bool manualSpell)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
// 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 != MWMechanics::getPlayer() && !manualSpell)
stats.getAiSequence().getCombatTargets(targetActors);
const float fCombatDistance = mStore.get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat();
osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();
// for player we can take faced object first
MWWorld::Ptr target;
if (actor == MWMechanics::getPlayer())
target = getFacedObject();
// if the faced object can not be activated, do not use it
if (!target.isEmpty() && !target.getClass().hasToolTip(target))
2018-10-09 10:21:12 +04:00
target = nullptr;
if (target.isEmpty())
{
// 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())
{
if (package->getTypeId() == MWMechanics::AiPackageTypeId::Cast)
{
2018-12-30 01:27:16 +03:00
target = package->getTarget();
break;
}
}
}
}
else
{
// For actor targets, we want to use melee hit contact.
// 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.
const std::pair<Ptr, osg::Vec3f> result1 = MWMechanics::getHitContact(actor, fCombatDistance);
// Get the target to use for "on touch" effects, using the facing direction from Head node
osg::Vec3f origin = getActorHeadTransform(actor).getTrans();
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));
osg::Vec3f direction = orient * osg::Vec3f(0, 1, 0);
float distance = getMaxActivationDistance();
osg::Vec3f dest = origin + direction * distance;
MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
float dist1 = std::numeric_limits<float>::max();
float dist2 = std::numeric_limits<float>::max();
if (!result1.first.isEmpty() && result1.first.getClass().isActor())
dist1 = (origin - result1.second).length();
if (result2.mHit)
dist2 = (origin - result2.mHitPointWorld).length();
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;
}
else if (result2.mHit)
{
target = result2.mHitObject;
hitPosition = result2.mHitPointWorld;
if (dist2 > getMaxActivationDistance() && !target.isEmpty()
&& !target.getClass().hasToolTip(target))
2018-10-09 10:21:12 +04:00
target = nullptr;
}
}
}
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();
MWMechanics::CastSpell cast(actor, target, false, manualSpell);
cast.mHitPosition = hitPosition;
if (!selectedSpell.empty())
{
const ESM::Spell* spell = mStore.get<ESM::Spell>().find(selectedSpell);
cast.cast(spell);
}
else if (actor.getClass().hasInventoryStore(actor))
{
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
if (inv.getSelectedEnchantItem() != inv.end())
{
const auto& itemPtr = *inv.getSelectedEnchantItem();
cast.cast(itemPtr);
}
}
}
void World::launchProjectile(MWWorld::Ptr& actor, MWWorld::Ptr& projectile, const osg::Vec3f& worldPos,
const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength)
{
// 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
bool underwater = isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
if (underwater)
{
MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);
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);
if (result.mHit)
MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength);
else
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);
}
void World::launchMagicBolt(
const ESM::RefId& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection, ESM::RefNum item)
{
mProjectileManager->launchMagicBolt(spellId, caster, fallbackDirection, item);
}
2013-12-08 23:36:37 +01:00
void World::updateProjectilesCasters()
{
mProjectileManager->updateCasters();
}
void World::applyLoopingParticles(const MWWorld::Ptr& ptr) const
{
const MWWorld::Class& cls = ptr.getClass();
if (cls.isActor())
{
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 = mStore.get<ESM::MagicEffect>().find(effect.mEffectId);
if (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx)
MWMechanics::playEffects(ptr, *magicEffect, false);
}
}
}
}
}
const std::vector<std::string>& World::getContentFiles() const
{
return mContentFiles;
}
2013-12-08 23:36:37 +01:00
void World::breakInvisibility(const Ptr& actor)
{
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(actor, ESM::MagicEffect::Invisibility);
// 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
}
bool World::useTorches() const
{
// If we are in exterior, check the weather manager.
// In interiors there are no precipitations and sun, so check the ambient
// Looks like pseudo-exteriors considered as interiors in this case
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
if (cell->isExterior())
{
float hour = getTimeStamp().getHour();
return mWeatherManager->useTorches(hour);
}
else
{
const MWWorld::Cell& cellVariant = *cell->getCell();
uint32_t ambient = cellVariant.getMood().mAmbiantColor;
int ambientTotal = (ambient & 0xff) + ((ambient >> 8) & 0xff) + ((ambient >> 16) & 0xff);
return !cell->getCell()->noSleep() && ambientTotal <= 201;
}
}
float World::getSunVisibility() const
{
return mWeatherManager->getSunVisibility();
}
float World::getSunPercentage() const
{
return mWeatherManager->getSunPercentage(getTimeStamp().getHour());
}
bool World::findInteriorPositionInWorldSpace(const MWWorld::CellStore* cell, osg::Vec3f& result)
{
if (cell->isExterior())
return false;
// 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.
std::set<ESM::RefId> checkedCells;
std::set<ESM::RefId> currentCells;
std::set<ESM::RefId> nextCells;
nextCells.insert(cell->getCell()->getId());
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
{
MWWorld::CellStore& next = mWorldModel.getCell(currentCell);
// Check if any door in the cell leads to an exterior directly
for (const MWWorld::LiveCellRef<ESM::Door>& ref : next.getReadOnlyDoors().mList)
{
if (!ref.mRef.getTeleport())
continue;
2023-04-13 17:44:23 +02:00
if (ref.mRef.getDestCell().is<ESM::ESM3ExteriorCellRefId>())
{
ESM::Position pos = ref.mRef.getDoorDest();
2015-06-03 19:41:19 +02:00
result = pos.asVec3();
return true;
}
else
{
ESM::RefId dest = ref.mRef.getDestCell();
if (!checkedCells.count(dest) && !currentCells.count(dest))
nextCells.insert(dest);
}
}
2019-01-07 20:35:36 +03:00
checkedCells.insert(currentCell);
}
}
// No luck :(
return false;
}
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)
{
if (ptr.getCell()->isExterior())
{
return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id);
}
// 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.
std::set<ESM::RefId> checkedCells;
std::set<ESM::RefId> currentCells;
std::set<ESM::RefId> nextCells;
2015-12-17 23:59:18 +01:00
MWWorld::ConstPtr closestMarker;
nextCells.insert(ptr.getCell()->getCell()->getId());
while (!nextCells.empty())
{
currentCells.clear();
std::swap(currentCells, nextCells);
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
{
MWWorld::CellStore& next = mWorldModel.getCell(cell);
2018-12-30 01:27:16 +03:00
checkedCells.insert(cell);
closestMarker = next.searchConst(id);
if (!closestMarker.isEmpty())
{
return closestMarker;
}
// Check if any door in the cell leads to an exterior directly
for (const MWWorld::LiveCellRef<ESM::Door>& ref : next.getReadOnlyDoors().mList)
{
if (!ref.mRef.getTeleport())
continue;
2023-04-13 17:43:13 +02:00
if (ref.mRef.getDestCell().is<ESM::ESM3ExteriorCellRefId>())
{
2015-06-03 19:41:19 +02:00
osg::Vec3f worldPos = ref.mRef.getDoorDest().asVec3();
return getClosestMarkerFromExteriorPosition(worldPos, id);
}
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();
if (!checkedCells.contains(dest) && !currentCells.contains(dest))
nextCells.insert(dest);
}
}
}
}
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();
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)
{
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();
if (distance < closestDistance)
{
closestDistance = distance;
2018-12-30 01:27:16 +03:00
closestMarker = marker;
}
}
return closestMarker;
}
void World::rest(double hours)
{
mWorldModel.forEachLoadedCellStore([hours](CellStore& store) { store.rest(hours); });
}
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
mWorldModel.forEachLoadedCellStore([duration](CellStore& store) { store.recharge(duration); });
}
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-12-17 23:59:18 +01:00
MWWorld::ConstPtr closestMarker = getClosestMarker(ptr, id);
if (closestMarker.isEmpty())
{
2018-08-14 23:05:43 +04:00
Log(Debug::Warning) << "Failed to teleport: no closest marker found";
return;
}
ESM::RefId cellId = closestMarker.mCell->getCell()->getId();
MWWorld::ActionTeleport action(cellId, closestMarker.getRefData().getPosition(), false);
action.execute(ptr);
}
void World::updateWeather(float duration, bool paused)
{
bool isExterior = isCellExterior() || isCellQuasiExterior();
if (mPlayer->wasTeleported())
{
mPlayer->setTeleported(false);
const ESM::RefId& playerRegion = getPlayerPtr().getCell()->getCell()->getRegion();
mWeatherManager->playerTeleported(playerRegion, isExterior);
}
const TimeStamp time = getTimeStamp();
mWeatherManager->update(duration, paused, time, isExterior);
}
2015-12-06 18:13:04 +01:00
struct AddDetectedReferenceVisitor
{
std::vector<Ptr>& mOut;
Ptr mDetector;
float mSquaredDist;
World::DetectionType mType;
const MWWorld::ESMStore& mStore;
2015-12-06 21:58:25 +01:00
bool operator()(const MWWorld::Ptr& ptr)
{
2015-06-03 21:37:21 +02:00
if ((ptr.getRefData().getPosition().asVec3() - mDetector.getRefData().getPosition().asVec3()).length2()
>= mSquaredDist)
return true;
if (!ptr.getRefData().isEnabled() || ptr.getRefData().isDeleted())
return true;
// Consider references inside containers as well (except if we are looking for a Creature, they cannot be in
// containers)
bool isContainer = ptr.getClass().getType() == ESM::Container::sRecordId;
if (mType != World::Detect_Creature && (ptr.getClass().isActor() || isContainer))
{
// but ignore containers without resolved content
if (isContainer && ptr.getRefData().getCustomData() == nullptr)
{
for (const auto& containerItem : ptr.get<ESM::Container>()->mBase->mInventory.mList)
{
if (containerItem.mCount)
{
try
{
ManualRef ref(mStore, containerItem.mItem, containerItem.mCount);
if (needToAdd(ref.getPtr(), mDetector))
{
mOut.push_back(ptr);
return true;
}
}
catch (const std::exception&)
{
// Ignore invalid item id
}
}
}
return true;
}
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
{
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (needToAdd(*it, mDetector))
{
mOut.push_back(ptr);
return true;
}
}
}
}
if (needToAdd(ptr, mDetector))
mOut.push_back(ptr);
return true;
}
2015-12-06 21:58:25 +01:00
bool needToAdd(const MWWorld::Ptr& ptr, const MWWorld::Ptr& detector)
{
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())
{
if (ptr.getClass().getType() != ESM::NPC::sRecordId)
return false;
}
else if (ptr.getClass().getType() != ESM::Creature::sRecordId)
return false;
if (ptr.getClass().getCreatureStats(ptr).isDead())
return false;
}
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)
2023-05-23 19:06:08 +02:00
dist = effects.getOrDefault(ESM::MagicEffect::DetectAnimal).getMagnitude();
else if (type == World::Detect_Key)
2023-05-23 19:06:08 +02:00
dist = effects.getOrDefault(ESM::MagicEffect::DetectKey).getMagnitude();
else if (type == World::Detect_Enchantment)
2023-05-23 19:06:08 +02:00
dist = effects.getOrDefault(ESM::MagicEffect::DetectEnchantment).getMagnitude();
if (!dist)
return;
dist = feetToGameUnits(dist);
AddDetectedReferenceVisitor visitor{ out, ptr, dist * dist, type, mStore };
2018-12-30 01:27:16 +03:00
for (CellStore* cellStore : mWorldScene->getActiveCells())
{
2015-12-06 18:13:04 +01:00
cellStore->forEach(visitor);
}
}
float World::feetToGameUnits(float feet)
{
// Original engine rounds size upward
static const int unitsPerFoot = ceil(Constants::UnitsPerFoot);
return feet * unitsPerFoot;
}
float World::getActivationDistancePlusTelekinesis()
{
float telekinesisRangeBonus = mPlayer->getPlayer()
.getClass()
.getCreatureStats(mPlayer->getPlayer())
.getMagicEffects()
2023-05-23 19:06:08 +02:00
.getOrDefault(ESM::MagicEffect::Telekinesis)
.getMagnitude();
telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus);
float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus;
return activationDistance;
}
MWWorld::Ptr World::getPlayerPtr()
{
return mPlayer->getPlayer();
}
MWWorld::ConstPtr World::getPlayerConstPtr() const
{
return mPlayer->getConstPlayer();
}
void World::updateDialogueGlobals()
{
MWWorld::Ptr player = getPlayerPtr();
int bounty = player.getClass().getNpcStats(player).getBounty();
int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId);
static float fCrimeGoldDiscountMult
= mStore.get<ESM::GameSetting>().find("fCrimeGoldDiscountMult")->mValue.getFloat();
static float fCrimeGoldTurnInMult
= mStore.get<ESM::GameSetting>().find("fCrimeGoldTurnInMult")->mValue.getFloat();
int discount = static_cast<int>(bounty * fCrimeGoldDiscountMult);
int turnIn = static_cast<int>(bounty * fCrimeGoldTurnInMult);
if (bounty > 0)
{
discount = std::max(1, discount);
turnIn = std::max(1, turnIn);
}
mGlobalVariables[Globals::sPCHasCrimeGold].setInteger((bounty <= playerGold) ? 1 : 0);
mGlobalVariables[Globals::sPCHasGoldDiscount].setInteger((discount <= playerGold) ? 1 : 0);
mGlobalVariables[Globals::sCrimeGoldDiscount].setInteger(discount);
mGlobalVariables[Globals::sCrimeGoldTurnIn].setInteger(turnIn);
mGlobalVariables[Globals::sPCHasTurnIn].setInteger((turnIn <= playerGold) ? 1 : 0);
}
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"));
if (prisonMarker.isEmpty())
{
2018-08-14 23:05:43 +04:00
Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found.";
return;
}
ESM::RefId prisonName = prisonMarker.getCellRef().getDestCell();
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";
return;
}
MWWorld::CellStore& prison = mWorldModel.getCell(prisonName);
2014-01-11 03:29:41 +01:00
MWWorld::Ptr closestChest = prison.search(ESM::RefId::stringRefId("stolen_goods"));
if (!closestChest.isEmpty()) // Found a close chest
2014-01-11 03:29:41 +01:00
{
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
2014-01-11 03:29:41 +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()
{
const MWWorld::Ptr player = getPlayerPtr();
2014-01-11 06:47:58 +01:00
if (!mGoToJail)
{
// 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;
mPlayerInJail = true;
int bounty = player.getClass().getNpcStats(player).getBounty();
player.getClass().getNpcStats(player).setBounty(0);
mPlayer->recordCrimeId();
confiscateStolenItems(player);
static int iDaysinPrisonMod = mStore.get<ESM::GameSetting>().find("iDaysinPrisonMod")->mValue.getInteger();
mDaysInPrison = std::max(1, bounty / iDaysinPrisonMod);
2014-01-11 06:47:58 +01:00
return;
}
else
{
if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(player))
{
player.getClass().getCreatureStats(player).setAttackingOrSpell(false);
}
mPlayer->setDrawState(MWMechanics::DrawState::Nothing);
2014-01-11 06:47:58 +01:00
mGoToJail = false;
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
MWBase::Environment::get().getWindowManager()->goToJail(mDaysInPrison);
2014-01-11 06:47:58 +01:00
}
}
bool World::isPlayerInJail() const
{
return mPlayerInJail;
}
void World::setPlayerTraveling(bool traveling)
{
mPlayerTraveling = traveling;
}
bool World::isPlayerTraveling() const
{
return mPlayerTraveling;
}
float World::getTerrainHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) const
2016-11-16 20:15:25 +01:00
{
return mRendering->getTerrainHeightAt(worldPos, worldspace);
2016-11-16 20:15:25 +01:00
}
osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const
2016-11-16 20:15:25 +01: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)
return mPhysics->getRenderingHalfExtents(object);
2016-11-16 20:15:25 +01:00
else
return mPhysics->getHalfExtents(object);
2016-11-16 20:15:25 +01:00
}
std::filesystem::path World::exportSceneGraph(const Ptr& ptr)
{
auto file = mUserDataPath / "openmw.osgt";
if (!ptr.isEmpty())
{
mRendering->pagingBlacklistObject(mStore.find(ptr.getCellRef().getRefId()), ptr);
mWorldScene->removeFromPagedRefs(ptr);
}
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)
{
const ESM::CreatureLevList* list = mStore.get<ESM::CreatureLevList>().find(creatureList);
static int iNumberCreatures = mStore.get<ESM::GameSetting>().find("iNumberCreatures")->mValue.getInteger();
int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures, mPrng); // [1, iNumberCreatures]
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);
if (selectedCreature.empty())
continue;
MWWorld::ManualRef ref(mStore, selectedCreature, 1);
safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f);
}
}
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
{
2023-07-16 20:46:54 +02:00
if (ptr == getPlayerPtr() && Settings::gui().mHitFader)
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)));
if (texture.empty())
texture = Fallback::Map::getString("Blood_Texture_0");
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]
mResourceSystem->getVFS());
2016-09-14 23:18:29 +09:00
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
2014-01-17 10:52:44 +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)
{
2018-07-15 12:44:25 +04:00
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);
}
2015-12-06 18:13:04 +01:00
struct ResetActorsVisitor
{
World& mWorld;
2021-06-23 23:13:59 +02:00
bool operator()(const Ptr& ptr)
{
if (ptr.getClass().isActor() && ptr.getCellRef().hasContentFile())
{
if (ptr.getCell()->movedHere(ptr))
return true;
const ESM::Position& origPos = ptr.getCellRef().getPosition();
mWorld.moveObject(ptr, origPos.asVec3());
mWorld.rotateObject(ptr, origPos.asRotationVec3());
ptr.getClass().adjustPosition(ptr, true);
}
return true;
}
};
void World::resetActors()
{
2018-12-30 01:27:16 +03:00
for (CellStore* cellstore : mWorldScene->getActiveCells())
{
ResetActorsVisitor visitor{ *this };
2015-12-06 18:13:04 +01:00
cellstore->forEach(visitor);
}
}
2014-10-05 22:24:11 +02:00
bool World::isWalkingOnWater(const ConstPtr& actor) const
2014-10-05 22:24:11 +02:00
{
const MWPhysics::Actor* physicActor = mPhysics->getActor(actor);
2014-10-05 22:24:11 +02:00
if (physicActor && physicActor->isWalkingOnWater())
return true;
return false;
}
2021-04-07 12:07:03 +04:00
osg::Vec3f World::aimToTarget(const ConstPtr& actor, const ConstPtr& target, bool isRangedCombat)
{
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);
return (targetPos - weaponPos);
}
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)
{
if (obj.empty())
return;
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&)
{
}
}
void World::preloadEffects(const ESM::EffectList* effectList)
{
for (const ESM::ENAMstruct& effectInfo : effectList->mList)
{
const ESM::MagicEffect* effect = mStore.get<ESM::MagicEffect>().find(effectInfo.mEffectID);
if (MWMechanics::isSummoningEffect(effectInfo.mEffectID))
{
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"));
preload(mWorldScene.get(), mStore, MWMechanics::getSummonedCreature(effectInfo.mEffectID));
}
preload(mWorldScene.get(), mStore, effect->mCasting);
preload(mWorldScene.get(), mStore, effect->mHit);
if (effectInfo.mArea > 0)
preload(mWorldScene.get(), mStore, effect->mArea);
if (effectInfo.mRange == ESM::RT_Target)
preload(mWorldScene.get(), mStore, effect->mBolt);
}
}
2018-03-14 01:49:08 +03:00
DetourNavigator::Navigator* World::getNavigator() const
{
return mNavigator.get();
}
void World::updateActorPath(const MWWorld::ConstPtr& actor, const std::deque<osg::Vec3f>& path,
const DetourNavigator::AgentBounds& agentBounds, const osg::Vec3f& start, const osg::Vec3f& end) const
{
mRendering->updateActorPath(actor, path, agentBounds, start, end);
}
void World::removeActorPath(const MWWorld::ConstPtr& actor) const
{
mRendering->removeActorPath(actor);
}
void World::setNavMeshNumberToRender(const std::size_t value)
{
mRendering->setNavMeshNumber(value);
}
DetourNavigator::AgentBounds World::getPathfindingAgentBounds(const MWWorld::ConstPtr& actor) const
{
const MWPhysics::Actor* physicsActor = mPhysics->getActor(actor);
if (physicsActor == nullptr || !actor.isInCell() || actor.getCell()->isExterior())
2023-06-27 23:41:06 +02:00
return DetourNavigator::AgentBounds{ Settings::game().mActorCollisionShapeType,
Settings::game().mDefaultActorPathfindHalfExtents };
else
return DetourNavigator::AgentBounds{ physicsActor->getCollisionShapeType(),
physicsActor->getHalfExtents() };
}
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);
const auto toLocal = object->getTransform().inverse();
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);
}
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
{
return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors);
}
void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{
DetourNavigator::reportStats(mNavigator->getStats(), frameNumber, stats);
mPhysics->reportStats(frameNumber, stats);
}
void World::updateSkyDate()
{
ESM::EpochTimeStamp currentDate = mTimeManager->getEpochTimeStamp();
mRendering->skySetDate(currentDate.mDay, currentDate.mMonth);
}
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)
{
2022-12-15 20:56:17 +01:00
return mWorldModel.getAll(id);
}
Misc::Rng::Generator& World::getPrng()
{
return mPrng;
}
2022-05-13 18:58:00 -07:00
MWRender::PostProcessor* World::getPostProcessor()
{
return mRendering->getPostProcessor();
}
void World::setActorActive(const MWWorld::Ptr& ptr, bool value)
{
if (MWPhysics::Actor* const actor = mPhysics->getActor(ptr))
actor->setActive(value);
}
}