2014-05-16 13:09:23 +02:00
|
|
|
#include "projectilemanager.hpp"
|
|
|
|
|
2016-09-05 02:52:00 +09:00
|
|
|
#include <iomanip>
|
2020-10-23 20:27:07 +02:00
|
|
|
#include <memory>
|
2021-01-07 11:02:53 +01:00
|
|
|
#include <optional>
|
2022-06-27 21:32:46 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
#include <osg/PositionAttitudeTransform>
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2018-08-14 23:05:43 +04:00
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/esmwriter.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadench.hpp>
|
|
|
|
#include <components/esm3/loadmgef.hpp>
|
|
|
|
#include <components/esm3/loadrace.hpp>
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/projectilestate.hpp>
|
2017-02-02 16:20:34 +09:00
|
|
|
|
2018-09-17 14:52:43 +04:00
|
|
|
#include <components/misc/constants.hpp>
|
2023-04-01 11:47:53 +04:00
|
|
|
#include <components/misc/convert.hpp>
|
2022-06-29 00:32:11 +02:00
|
|
|
#include <components/misc/resourcehelpers.hpp>
|
2018-09-17 14:52:43 +04:00
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
#include <components/resource/resourcesystem.hpp>
|
|
|
|
#include <components/resource/scenemanager.hpp>
|
2017-02-02 16:20:34 +09:00
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
#include <components/sceneutil/controller.hpp>
|
2015-12-05 00:44:04 +01:00
|
|
|
#include <components/sceneutil/lightmanager.hpp>
|
2021-10-05 12:21:12 +00:00
|
|
|
#include <components/sceneutil/nodecallback.hpp>
|
2015-12-05 00:04:23 +01:00
|
|
|
#include <components/sceneutil/visitor.hpp>
|
2014-05-17 05:21:17 +02:00
|
|
|
|
2023-06-27 23:41:06 +02:00
|
|
|
#include <components/settings/values.hpp>
|
2021-11-13 00:23:16 +03:00
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
#include "../mwworld/class.hpp"
|
2015-02-09 15:01:49 +01:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2014-05-17 02:52:10 +02:00
|
|
|
#include "../mwworld/inventorystore.hpp"
|
2014-05-16 13:09:23 +02:00
|
|
|
#include "../mwworld/manualref.hpp"
|
|
|
|
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwbase/soundmanager.hpp"
|
2022-05-13 19:28:53 +02:00
|
|
|
#include "../mwbase/windowmanager.hpp"
|
2014-05-16 13:09:23 +02:00
|
|
|
#include "../mwbase/world.hpp"
|
|
|
|
|
2015-08-21 21:12:39 +12:00
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
2014-05-16 13:09:23 +02:00
|
|
|
#include "../mwmechanics/combat.hpp"
|
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
|
|
|
#include "../mwmechanics/spellcasting.hpp"
|
2018-12-26 13:45:28 +04:00
|
|
|
#include "../mwmechanics/weapontype.hpp"
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2014-08-18 15:32:52 +02:00
|
|
|
#include "../mwrender/animation.hpp"
|
2015-12-04 23:28:11 +01:00
|
|
|
#include "../mwrender/renderingmanager.hpp"
|
2016-09-08 01:39:46 +09:00
|
|
|
#include "../mwrender/util.hpp"
|
2020-04-20 18:47:14 +02:00
|
|
|
#include "../mwrender/vismask.hpp"
|
2014-05-16 13:09:23 +02:00
|
|
|
|
|
|
|
#include "../mwsound/sound.hpp"
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
#include "../mwphysics/physicssystem.hpp"
|
2020-10-23 20:27:07 +02:00
|
|
|
#include "../mwphysics/projectile.hpp"
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2016-09-05 04:22:57 +09:00
|
|
|
namespace
|
|
|
|
{
|
2022-10-18 09:26:55 +02:00
|
|
|
ESM::EffectList getMagicBoltData(std::vector<ESM::RefId>& projectileIDs, std::set<ESM::RefId>& sounds, float& speed,
|
|
|
|
std::string& texture, std::string& sourceName, const ESM::RefId& id)
|
2016-09-05 04:22:57 +09:00
|
|
|
{
|
2023-04-20 21:07:53 +02:00
|
|
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
2017-09-20 18:56:32 +02:00
|
|
|
const ESM::EffectList* effects;
|
|
|
|
if (const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(id)) // check if it's a spell
|
|
|
|
{
|
|
|
|
sourceName = spell->mName;
|
|
|
|
effects = &spell->mEffects;
|
|
|
|
}
|
|
|
|
else // check if it's an enchanted item
|
|
|
|
{
|
|
|
|
MWWorld::ManualRef ref(esmStore, id);
|
|
|
|
MWWorld::Ptr ptr = ref.getPtr();
|
|
|
|
const ESM::Enchantment* ench = esmStore.get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));
|
|
|
|
sourceName = ptr.getClass().getName(ptr);
|
|
|
|
effects = &ench->mEffects;
|
|
|
|
}
|
|
|
|
|
2016-09-05 04:22:57 +09:00
|
|
|
int count = 0;
|
2016-09-15 01:44:53 +09:00
|
|
|
speed = 0.0f;
|
2016-09-05 04:22:57 +09:00
|
|
|
ESM::EffectList projectileEffects;
|
2017-09-20 18:56:32 +02:00
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter(effects->mList.begin()); iter != effects->mList.end();
|
|
|
|
++iter)
|
2016-09-05 04:22:57 +09:00
|
|
|
{
|
|
|
|
const ESM::MagicEffect* magicEffect
|
2023-04-20 21:07:53 +02:00
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(iter->mEffectID);
|
2016-09-05 04:22:57 +09:00
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
// Speed of multi-effect projectiles should be the average of the constituent effects,
|
|
|
|
// based on observation of the original engine.
|
2016-09-05 04:22:57 +09:00
|
|
|
speed += magicEffect->mData.mSpeed;
|
|
|
|
count++;
|
|
|
|
|
|
|
|
if (iter->mRange != ESM::RT_Target)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (magicEffect->mBolt.empty())
|
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
|
|
|
projectileIDs.emplace_back(ESM::RefId::stringRefId("VFX_DefaultBolt"));
|
2016-09-05 04:22:57 +09:00
|
|
|
else
|
|
|
|
projectileIDs.push_back(magicEffect->mBolt);
|
|
|
|
|
|
|
|
if (!magicEffect->mBoltSound.empty())
|
2019-04-17 19:23:50 +03:00
|
|
|
sounds.emplace(magicEffect->mBoltSound);
|
2016-09-05 04:22:57 +09:00
|
|
|
else
|
2023-06-26 20:42:52 +02:00
|
|
|
sounds.emplace(MWBase::Environment::get()
|
|
|
|
.getESMStore()
|
|
|
|
->get<ESM::Skill>()
|
|
|
|
.find(magicEffect->mData.mSchool)
|
|
|
|
->mSchool->mBoltSound);
|
2016-09-05 04:22:57 +09:00
|
|
|
projectileEffects.mList.push_back(*iter);
|
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2016-09-05 04:22:57 +09:00
|
|
|
if (count != 0)
|
|
|
|
speed /= count;
|
2016-09-08 01:39:46 +09:00
|
|
|
|
2016-09-12 19:20:33 +09:00
|
|
|
// the particle texture is only used if there is only one projectile
|
2016-09-08 02:24:47 +09:00
|
|
|
if (projectileEffects.mList.size() == 1)
|
2016-09-08 01:39:46 +09:00
|
|
|
{
|
|
|
|
const ESM::MagicEffect* magicEffect
|
2023-04-20 21:07:53 +02:00
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(
|
2017-09-20 18:56:32 +02:00
|
|
|
effects->mList.begin()->mEffectID);
|
2016-09-12 19:20:33 +09:00
|
|
|
texture = magicEffect->mParticle;
|
2016-09-08 01:39:46 +09:00
|
|
|
}
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2016-09-05 04:22:57 +09:00
|
|
|
if (projectileEffects.mList.size()
|
|
|
|
> 1) // insert a VFX_Multiple projectile if there are multiple projectile effects
|
|
|
|
{
|
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 ID = ESM::RefId::stringRefId("VFX_Multiple" + std::to_string(effects->mList.size()));
|
|
|
|
std::vector<ESM::RefId>::iterator it;
|
2016-09-05 04:22:57 +09:00
|
|
|
it = projectileIDs.begin();
|
2019-01-07 17:47:39 +04:00
|
|
|
it = projectileIDs.insert(it, ID);
|
2016-09-05 04:22:57 +09:00
|
|
|
}
|
|
|
|
return projectileEffects;
|
|
|
|
}
|
2016-12-13 18:04:20 -07:00
|
|
|
|
|
|
|
osg::Vec4 getMagicBoltLightDiffuseColor(const ESM::EffectList& effects)
|
|
|
|
{
|
|
|
|
// Calculate combined light diffuse color from magical effects
|
|
|
|
osg::Vec4 lightDiffuseColor;
|
|
|
|
float lightDiffuseRed = 0.0f;
|
|
|
|
float lightDiffuseGreen = 0.0f;
|
|
|
|
float lightDiffuseBlue = 0.0f;
|
|
|
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter(effects.mList.begin()); iter != effects.mList.end();
|
|
|
|
++iter)
|
|
|
|
{
|
|
|
|
const ESM::MagicEffect* magicEffect
|
2023-04-20 21:07:53 +02:00
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(iter->mEffectID);
|
2016-12-13 18:04:20 -07:00
|
|
|
lightDiffuseRed += (static_cast<float>(magicEffect->mData.mRed) / 255.f);
|
|
|
|
lightDiffuseGreen += (static_cast<float>(magicEffect->mData.mGreen) / 255.f);
|
|
|
|
lightDiffuseBlue += (static_cast<float>(magicEffect->mData.mBlue) / 255.f);
|
|
|
|
}
|
|
|
|
int numberOfEffects = effects.mList.size();
|
|
|
|
lightDiffuseColor = osg::Vec4(lightDiffuseRed / numberOfEffects, lightDiffuseGreen / numberOfEffects,
|
|
|
|
lightDiffuseBlue / numberOfEffects, 1.0f);
|
|
|
|
|
|
|
|
return lightDiffuseColor;
|
|
|
|
}
|
2016-09-05 04:22:57 +09:00
|
|
|
}
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
namespace MWWorld
|
|
|
|
{
|
|
|
|
|
2015-12-04 23:28:11 +01:00
|
|
|
ProjectileManager::ProjectileManager(osg::Group* parent, Resource::ResourceSystem* resourceSystem,
|
|
|
|
MWRender::RenderingManager* rendering, MWPhysics::PhysicsSystem* physics)
|
2015-06-01 21:41:13 +02:00
|
|
|
: mParent(parent)
|
|
|
|
, mResourceSystem(resourceSystem)
|
2015-12-04 23:28:11 +01:00
|
|
|
, mRendering(rendering)
|
2015-06-01 21:41:13 +02:00
|
|
|
, mPhysics(physics)
|
2017-09-23 18:54:17 +02:00
|
|
|
, mCleanupTimer(0.0f)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-12-05 00:03:25 +01:00
|
|
|
/// Rotates an osg::PositionAttitudeTransform over time.
|
2021-10-05 12:21:12 +00:00
|
|
|
class RotateCallback : public SceneUtil::NodeCallback<RotateCallback, osg::PositionAttitudeTransform*>
|
2015-12-05 00:03:25 +01:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
RotateCallback(const osg::Vec3f& axis = osg::Vec3f(0, -1, 0), float rotateSpeed = osg::PI * 2)
|
|
|
|
: mAxis(axis)
|
|
|
|
, mRotateSpeed(rotateSpeed)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-10-05 12:21:12 +00:00
|
|
|
void operator()(osg::PositionAttitudeTransform* node, osg::NodeVisitor* nv)
|
2015-12-05 00:03:25 +01:00
|
|
|
{
|
|
|
|
double time = nv->getFrameStamp()->getSimulationTime();
|
|
|
|
|
|
|
|
osg::Quat orient = osg::Quat(time * mRotateSpeed, mAxis);
|
2021-10-05 12:21:12 +00:00
|
|
|
node->setAttitude(orient);
|
2015-12-05 00:03:25 +01:00
|
|
|
|
|
|
|
traverse(node, nv);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
osg::Vec3f mAxis;
|
|
|
|
float mRotateSpeed;
|
|
|
|
};
|
|
|
|
|
2021-02-05 12:12:34 +01:00
|
|
|
void ProjectileManager::createModel(State& state, const std::string& model, const osg::Vec3f& pos,
|
2016-12-09 18:10:06 -07:00
|
|
|
const osg::Quat& orient, bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mNode = new osg::PositionAttitudeTransform;
|
2020-04-20 18:47:14 +02:00
|
|
|
state.mNode->setNodeMask(MWRender::Mask_Effect);
|
2015-06-11 16:40:26 +02:00
|
|
|
state.mNode->setPosition(pos);
|
|
|
|
state.mNode->setAttitude(orient);
|
2016-12-03 19:12:25 -07:00
|
|
|
|
2016-12-03 19:09:03 -07:00
|
|
|
osg::Group* attachTo = state.mNode;
|
2015-12-05 00:03:25 +01:00
|
|
|
|
|
|
|
if (rotate)
|
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::PositionAttitudeTransform> rotateNode(new osg::PositionAttitudeTransform);
|
|
|
|
rotateNode->addUpdateCallback(new RotateCallback());
|
|
|
|
state.mNode->addChild(rotateNode);
|
|
|
|
attachTo = rotateNode;
|
|
|
|
}
|
|
|
|
|
2016-09-12 19:20:33 +09:00
|
|
|
osg::ref_ptr<osg::Node> projectile = mResourceSystem->getSceneManager()->getInstance(model, attachTo);
|
2015-12-05 00:03:25 +01:00
|
|
|
|
2016-09-04 17:39:14 +09:00
|
|
|
if (state.mIdMagic.size() > 1)
|
2022-06-29 00:32:11 +02:00
|
|
|
{
|
|
|
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
|
|
|
|
2016-09-04 17:39:14 +09:00
|
|
|
for (size_t iter = 1; iter != state.mIdMagic.size(); ++iter)
|
|
|
|
{
|
2016-09-05 02:52:00 +09:00
|
|
|
std::ostringstream nodeName;
|
|
|
|
nodeName << "Dummy" << std::setw(2) << std::setfill('0') << iter;
|
2023-04-20 21:07:53 +02:00
|
|
|
const ESM::Weapon* weapon
|
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::Weapon>().find(state.mIdMagic.at(iter));
|
2022-08-21 18:53:38 +02:00
|
|
|
std::string nameToFind = nodeName.str();
|
|
|
|
SceneUtil::FindByNameVisitor findVisitor(nameToFind);
|
2016-09-05 02:59:33 +09:00
|
|
|
attachTo->accept(findVisitor);
|
|
|
|
if (findVisitor.mFoundNode)
|
2022-05-13 19:28:53 +02:00
|
|
|
mResourceSystem->getSceneManager()->getInstance(
|
2022-06-29 00:32:11 +02:00
|
|
|
Misc::ResourceHelpers::correctMeshPath(weapon->mModel, vfs), findVisitor.mFoundNode);
|
2016-09-04 17:39:14 +09:00
|
|
|
}
|
2022-06-29 00:32:11 +02:00
|
|
|
}
|
2016-09-04 17:39:14 +09:00
|
|
|
|
2016-12-04 16:11:21 -07:00
|
|
|
if (createLight)
|
2016-12-03 19:09:03 -07:00
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Light> projectileLight(new osg::Light);
|
2016-12-03 19:44:52 -07:00
|
|
|
projectileLight->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
|
2016-12-04 16:11:21 -07:00
|
|
|
projectileLight->setDiffuse(lightDiffuseColor);
|
|
|
|
projectileLight->setSpecular(osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
2016-12-03 19:44:52 -07:00
|
|
|
projectileLight->setConstantAttenuation(0.f);
|
|
|
|
projectileLight->setLinearAttenuation(0.1f);
|
|
|
|
projectileLight->setQuadraticAttenuation(0.f);
|
2016-12-03 19:09:03 -07:00
|
|
|
projectileLight->setPosition(osg::Vec4(pos, 1.0));
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2016-12-03 19:09:03 -07:00
|
|
|
SceneUtil::LightSource* projectileLightSource = new SceneUtil::LightSource;
|
2020-04-20 18:47:14 +02:00
|
|
|
projectileLightSource->setNodeMask(MWRender::Mask_Lighting);
|
2016-12-03 19:09:03 -07:00
|
|
|
projectileLightSource->setRadius(66.f);
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2016-12-03 19:09:03 -07:00
|
|
|
state.mNode->addChild(projectileLightSource);
|
|
|
|
projectileLightSource->setLight(projectileLight);
|
|
|
|
}
|
2015-12-05 00:04:23 +01:00
|
|
|
|
2015-12-05 00:44:04 +01:00
|
|
|
state.mNode->addCullCallback(new SceneUtil::LightListCallback);
|
|
|
|
|
2015-12-05 00:03:25 +01:00
|
|
|
mParent->addChild(state.mNode);
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2022-04-03 23:23:25 +02:00
|
|
|
state.mEffectAnimationTime = std::make_shared<MWRender::EffectAnimationTime>();
|
2015-06-01 21:41:13 +02:00
|
|
|
|
|
|
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime);
|
|
|
|
state.mNode->accept(assignVisitor);
|
2016-09-12 19:20:33 +09:00
|
|
|
|
|
|
|
MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
void ProjectileManager::update(State& state, float duration)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mEffectAnimationTime->addTime(duration);
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|
|
|
|
|
2021-08-27 20:07:50 +02:00
|
|
|
void ProjectileManager::launchMagicBolt(
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& spellId, const Ptr& caster, const osg::Vec3f& fallbackDirection, int slot)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
2016-06-10 23:33:47 +02:00
|
|
|
osg::Vec3f pos = caster.getRefData().getPosition().asVec3();
|
2016-03-05 15:56:54 +01:00
|
|
|
if (caster.getClass().isActor())
|
2016-06-10 23:33:47 +02:00
|
|
|
{
|
|
|
|
// Note: we ignore the collision box offset, this is required to make some flying creatures work as
|
|
|
|
// intended.
|
2021-03-24 10:21:37 -07:00
|
|
|
pos.z() += mPhysics->getRenderingHalfExtents(caster).z() * 2 * Constants::TorsoHeight;
|
2016-06-10 23:33:47 +02:00
|
|
|
}
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2014-06-28 14:49:07 +02:00
|
|
|
if (MWBase::Environment::get().getWorld()->isUnderwater(
|
|
|
|
caster.getCell(), pos)) // Underwater casting not possible
|
|
|
|
return;
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Quat orient;
|
2014-06-18 01:41:07 +02:00
|
|
|
if (caster.getClass().isActor())
|
2015-06-01 21:41:13 +02:00
|
|
|
orient = osg::Quat(caster.getRefData().getPosition().rot[0], osg::Vec3f(-1, 0, 0))
|
|
|
|
* osg::Quat(caster.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, -1));
|
2014-06-18 01:41:07 +02:00
|
|
|
else
|
2015-06-01 21:41:13 +02:00
|
|
|
orient.makeRotate(osg::Vec3f(0, 1, 0), osg::Vec3f(fallbackDirection));
|
2014-05-16 13:09:23 +02:00
|
|
|
|
|
|
|
MagicBoltState state;
|
2014-05-17 05:21:17 +02:00
|
|
|
state.mSpellId = spellId;
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mCasterHandle = caster;
|
2021-08-27 20:07:50 +02:00
|
|
|
state.mSlot = slot;
|
2014-06-18 01:41:07 +02:00
|
|
|
if (caster.getClass().isActor())
|
|
|
|
state.mActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
|
|
|
else
|
|
|
|
state.mActorId = -1;
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
std::string texture;
|
2016-09-08 01:39:46 +09:00
|
|
|
|
2017-09-20 18:56:32 +02:00
|
|
|
state.mEffects = getMagicBoltData(
|
|
|
|
state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2016-09-05 04:22:57 +09:00
|
|
|
// Non-projectile should have been removed by getMagicBoltData
|
2016-09-05 00:04:11 +02:00
|
|
|
if (state.mEffects.mList.empty())
|
2016-09-05 04:22:57 +09:00
|
|
|
return;
|
|
|
|
|
2020-04-04 22:39:13 +04:00
|
|
|
if (!caster.getClass().isActor() && fallbackDirection.length2() <= 0)
|
|
|
|
{
|
|
|
|
Log(Debug::Warning) << "Unable to launch magic bolt (direction to target is empty)";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-20 21:07:53 +02:00
|
|
|
MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), state.mIdMagic.at(0));
|
2016-09-04 17:39:14 +09:00
|
|
|
MWWorld::Ptr ptr = ref.getPtr();
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2017-09-20 18:56:32 +02:00
|
|
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2021-02-05 12:12:34 +01:00
|
|
|
auto model = ptr.getClass().getModel(ptr);
|
|
|
|
createModel(state, model, pos, orient, true, true, lightDiffuseColor, texture);
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2016-09-04 17:39:14 +09:00
|
|
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
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& soundid : state.mSoundIds)
|
2016-09-04 23:57:06 +09:00
|
|
|
{
|
2019-04-17 19:23:50 +03:00
|
|
|
MWBase::Sound* sound
|
|
|
|
= sndMgr->playSound3D(pos, soundid, 1.0f, 1.0f, MWSound::Type::Sfx, MWSound::PlayMode::Loop);
|
2017-01-01 21:34:31 +01:00
|
|
|
if (sound)
|
|
|
|
state.mSounds.push_back(sound);
|
2016-09-04 23:57:06 +09:00
|
|
|
}
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2021-02-05 12:12:34 +01:00
|
|
|
// in case there are multiple effects, the model is a dummy without geometry. Use the second effect for physics
|
|
|
|
// shape
|
|
|
|
if (state.mIdMagic.size() > 1)
|
2022-06-29 00:32:11 +02:00
|
|
|
{
|
|
|
|
model = Misc::ResourceHelpers::correctMeshPath(
|
2023-04-20 21:07:53 +02:00
|
|
|
MWBase::Environment::get().getESMStore()->get<ESM::Weapon>().find(state.mIdMagic[1])->mModel,
|
2022-06-29 00:32:11 +02:00
|
|
|
MWBase::Environment::get().getResourceSystem()->getVFS());
|
|
|
|
}
|
2021-08-31 16:25:45 +02:00
|
|
|
state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true);
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = false;
|
2014-05-16 13:09:23 +02:00
|
|
|
mMagicBolts.push_back(state);
|
|
|
|
}
|
|
|
|
|
2021-06-23 23:13:59 +02:00
|
|
|
void ProjectileManager::launchProjectile(const Ptr& actor, const ConstPtr& projectile, const osg::Vec3f& pos,
|
|
|
|
const osg::Quat& orient, const Ptr& bow, float speed, float attackStrength)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
|
|
|
ProjectileState state;
|
|
|
|
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
2014-05-25 14:13:07 +02:00
|
|
|
state.mBowId = bow.getCellRef().getRefId();
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mVelocity = orient * osg::Vec3f(0, 1, 0) * speed;
|
2016-09-04 01:54:09 +09:00
|
|
|
state.mIdArrow = projectile.getCellRef().getRefId();
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mCasterHandle = actor;
|
2015-06-26 05:15:07 +02:00
|
|
|
state.mAttackStrength = attackStrength;
|
2018-12-26 13:45:28 +04:00
|
|
|
int type = projectile.get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown;
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2023-04-20 21:07:53 +02:00
|
|
|
MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), projectile.getCellRef().getRefId());
|
2014-05-16 13:09:23 +02:00
|
|
|
MWWorld::Ptr ptr = ref.getPtr();
|
|
|
|
|
2021-02-05 12:12:34 +01:00
|
|
|
const auto model = ptr.getClass().getModel(ptr);
|
|
|
|
createModel(state, model, pos, orient, false, false, osg::Vec4(0, 0, 0, 0));
|
2019-08-07 11:03:26 +04:00
|
|
|
if (!ptr.getClass().getEnchantment(ptr).empty())
|
|
|
|
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2021-08-31 16:25:45 +02:00
|
|
|
state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false);
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = false;
|
2014-05-16 13:09:23 +02:00
|
|
|
mProjectiles.push_back(state);
|
|
|
|
}
|
|
|
|
|
2021-02-25 23:12:14 +00:00
|
|
|
void ProjectileManager::updateCasters()
|
|
|
|
{
|
|
|
|
for (auto& state : mProjectiles)
|
|
|
|
mPhysics->setCaster(state.mProjectileId, state.getCaster());
|
|
|
|
|
|
|
|
for (auto& state : mMagicBolts)
|
|
|
|
{
|
|
|
|
// casters are identified by actor id in the savegame. objects doesn't have one so they can't be identified
|
|
|
|
// back.
|
|
|
|
// TODO: should object-type caster be restored from savegame?
|
|
|
|
if (state.mActorId == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto caster = state.getCaster();
|
|
|
|
if (caster.isEmpty())
|
|
|
|
{
|
|
|
|
Log(Debug::Error) << "Couldn't find caster with ID " << state.mActorId;
|
|
|
|
cleanupMagicBolt(state);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
mPhysics->setCaster(state.mProjectileId, caster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
void ProjectileManager::update(float dt)
|
|
|
|
{
|
2017-09-23 18:54:17 +02:00
|
|
|
periodicCleanup(dt);
|
2014-05-16 13:09:23 +02:00
|
|
|
moveProjectiles(dt);
|
|
|
|
moveMagicBolts(dt);
|
|
|
|
}
|
|
|
|
|
2017-09-23 18:54:17 +02:00
|
|
|
void ProjectileManager::periodicCleanup(float dt)
|
|
|
|
{
|
|
|
|
mCleanupTimer -= dt;
|
|
|
|
if (mCleanupTimer <= 0.0f)
|
|
|
|
{
|
|
|
|
mCleanupTimer = 2.0f;
|
|
|
|
|
|
|
|
auto isCleanable = [](const ProjectileManager::State& state) -> bool {
|
|
|
|
const float farawayThreshold = 72000.0f;
|
|
|
|
osg::Vec3 playerPos = MWMechanics::getPlayer().getRefData().getPosition().asVec3();
|
|
|
|
return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold * farawayThreshold;
|
|
|
|
};
|
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& projectileState : mProjectiles)
|
2017-09-23 18:54:17 +02:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
if (isCleanable(projectileState))
|
|
|
|
cleanupProjectile(projectileState);
|
2017-09-23 18:54:17 +02:00
|
|
|
}
|
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& magicBoltState : mMagicBolts)
|
2017-09-23 18:54:17 +02:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
if (isCleanable(magicBoltState))
|
|
|
|
cleanupMagicBolt(magicBoltState);
|
2017-09-23 18:54:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
void ProjectileManager::moveMagicBolts(float duration)
|
|
|
|
{
|
2023-06-27 23:41:06 +02:00
|
|
|
const bool normaliseRaceSpeed = Settings::game().mNormaliseRaceSpeed;
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& magicBoltState : mMagicBolts)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
if (magicBoltState.mToDelete)
|
|
|
|
continue;
|
|
|
|
|
2020-12-14 22:23:01 +01:00
|
|
|
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
2020-10-23 20:27:07 +02:00
|
|
|
if (!projectile->isActive())
|
|
|
|
continue;
|
2020-06-06 19:09:18 +03:00
|
|
|
// If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame.
|
2020-10-23 20:27:07 +02:00
|
|
|
MWWorld::Ptr caster = magicBoltState.getCaster();
|
2020-06-06 19:09:18 +03:00
|
|
|
if (!caster.isEmpty() && caster.getClass().isActor())
|
|
|
|
{
|
|
|
|
if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())
|
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
cleanupMagicBolt(magicBoltState);
|
2020-06-06 19:09:18 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 21:07:53 +02:00
|
|
|
const auto& store = *MWBase::Environment::get().getESMStore();
|
2020-10-23 20:27:07 +02:00
|
|
|
osg::Quat orient = magicBoltState.mNode->getAttitude();
|
2021-11-13 00:23:16 +03:00
|
|
|
static float fTargetSpellMaxSpeed
|
|
|
|
= store.get<ESM::GameSetting>().find("fTargetSpellMaxSpeed")->mValue.getFloat();
|
2020-10-23 20:27:07 +02:00
|
|
|
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
2021-11-13 00:23:16 +03:00
|
|
|
if (!normaliseRaceSpeed && !caster.isEmpty() && caster.getClass().isNpc())
|
|
|
|
{
|
|
|
|
const auto npc = caster.get<ESM::NPC>()->mBase;
|
|
|
|
const auto race = store.get<ESM::Race>().find(npc->mRace);
|
|
|
|
speed *= npc->isMale() ? race->mData.mWeight.mMale : race->mData.mWeight.mFemale;
|
|
|
|
}
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Vec3f direction = orient * osg::Vec3f(0, 1, 0);
|
|
|
|
direction.normalize();
|
2021-10-09 18:16:29 +02:00
|
|
|
projectile->setVelocity(direction * speed);
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
update(magicBoltState, duration);
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit
|
|
|
|
// result.
|
|
|
|
std::vector<MWWorld::Ptr> targetActors;
|
|
|
|
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
|
|
|
|
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
2020-12-14 22:23:01 +01:00
|
|
|
projectile->setValidTargets(targetActors);
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectileManager::moveProjectiles(float duration)
|
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& projectileState : mProjectiles)
|
2014-05-16 13:09:23 +02:00
|
|
|
{
|
2021-01-24 15:15:51 +01:00
|
|
|
if (projectileState.mToDelete)
|
|
|
|
continue;
|
|
|
|
|
2020-12-14 22:23:01 +01:00
|
|
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
2020-10-23 20:27:07 +02:00
|
|
|
if (!projectile->isActive())
|
|
|
|
continue;
|
2014-05-16 13:09:23 +02:00
|
|
|
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
|
|
|
// simulating aerodynamics at all
|
2020-10-23 20:27:07 +02:00
|
|
|
projectileState.mVelocity
|
|
|
|
-= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2021-10-09 18:16:29 +02:00
|
|
|
projectile->setVelocity(projectileState.mVelocity);
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2019-03-05 13:33:58 +04:00
|
|
|
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting
|
|
|
|
// direction.
|
2020-10-23 20:27:07 +02:00
|
|
|
if (!projectileState.mThrown)
|
2019-03-05 13:33:58 +04:00
|
|
|
{
|
|
|
|
osg::Quat orient;
|
2020-10-23 20:27:07 +02:00
|
|
|
orient.makeRotate(osg::Vec3f(0, 1, 0), projectileState.mVelocity);
|
|
|
|
projectileState.mNode->setAttitude(orient);
|
2019-03-05 13:33:58 +04:00
|
|
|
}
|
2017-11-23 00:32:22 +01:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
update(projectileState, duration);
|
2015-06-01 21:41:13 +02:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
MWWorld::Ptr caster = projectileState.getCaster();
|
2014-05-16 13:09:23 +02:00
|
|
|
|
2017-02-02 16:20:34 +09:00
|
|
|
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit
|
|
|
|
// result.
|
|
|
|
std::vector<MWWorld::Ptr> targetActors;
|
|
|
|
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
|
|
|
|
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
2020-12-14 22:23:01 +01:00
|
|
|
projectile->setValidTargets(targetActors);
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
void ProjectileManager::processHits()
|
2019-02-13 11:30:16 +04:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& projectileState : mProjectiles)
|
2019-02-13 11:30:16 +04:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
if (projectileState.mToDelete)
|
|
|
|
continue;
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
2021-01-07 11:02:53 +01:00
|
|
|
|
2021-10-09 18:16:29 +02:00
|
|
|
const auto pos = projectile->getSimulationPosition();
|
2021-01-07 11:02:53 +01:00
|
|
|
projectileState.mNode->setPosition(pos);
|
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
if (projectile->isActive())
|
|
|
|
continue;
|
2021-01-07 11:02:53 +01:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
const auto target = projectile->getTarget();
|
2021-01-07 11:02:53 +01:00
|
|
|
auto caster = projectileState.getCaster();
|
2020-11-21 16:26:45 +01:00
|
|
|
assert(target != caster);
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
if (caster.isEmpty())
|
|
|
|
caster = target;
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
// Try to get a Ptr to the bow that was used. It might no longer exist.
|
2023-04-20 21:07:53 +02:00
|
|
|
MWWorld::ManualRef projectileRef(*MWBase::Environment::get().getESMStore(), projectileState.mIdArrow);
|
2020-10-23 20:27:07 +02:00
|
|
|
MWWorld::Ptr bow = projectileRef.getPtr();
|
|
|
|
if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId)
|
|
|
|
{
|
|
|
|
MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster);
|
|
|
|
MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2022-10-18 09:26:55 +02:00
|
|
|
if (invIt != inv.end() && invIt->getCellRef().getRefId() == projectileState.mBowId)
|
2020-10-23 20:27:07 +02:00
|
|
|
bow = *invIt;
|
2019-02-13 11:30:16 +04:00
|
|
|
}
|
2023-04-01 11:47:53 +04:00
|
|
|
|
|
|
|
const auto hitPosition = Misc::Convert::toOsg(projectile->getHitPosition());
|
|
|
|
|
2021-08-31 16:25:45 +02:00
|
|
|
if (projectile->getHitWater())
|
2023-04-01 11:47:53 +04:00
|
|
|
mRendering->emitWaterRipple(hitPosition);
|
2020-10-23 20:27:07 +02:00
|
|
|
|
|
|
|
MWMechanics::projectileHit(
|
2023-04-01 11:47:53 +04:00
|
|
|
caster, target, bow, projectileRef.getPtr(), hitPosition, projectileState.mAttackStrength);
|
2021-08-05 10:55:19 +02:00
|
|
|
projectileState.mToDelete = true;
|
2019-02-13 11:30:16 +04:00
|
|
|
}
|
2023-04-20 21:07:53 +02:00
|
|
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& magicBoltState : mMagicBolts)
|
2019-02-13 11:30:16 +04:00
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
if (magicBoltState.mToDelete)
|
|
|
|
continue;
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
2021-01-07 11:02:53 +01:00
|
|
|
|
2021-10-09 18:16:29 +02:00
|
|
|
const auto pos = projectile->getSimulationPosition();
|
2021-01-07 11:02:53 +01:00
|
|
|
magicBoltState.mNode->setPosition(pos);
|
|
|
|
for (const auto& sound : magicBoltState.mSounds)
|
|
|
|
sound->setPosition(pos);
|
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
if (projectile->isActive())
|
|
|
|
continue;
|
2021-01-07 11:02:53 +01:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
const auto target = projectile->getTarget();
|
2021-01-07 11:02:53 +01:00
|
|
|
const auto caster = magicBoltState.getCaster();
|
2020-11-21 16:26:45 +01:00
|
|
|
assert(target != caster);
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2020-10-23 20:27:07 +02:00
|
|
|
MWMechanics::CastSpell cast(caster, target);
|
2023-04-01 11:47:53 +04:00
|
|
|
cast.mHitPosition = Misc::Convert::toOsg(projectile->getHitPosition());
|
2020-10-23 20:27:07 +02:00
|
|
|
cast.mId = magicBoltState.mSpellId;
|
|
|
|
cast.mSourceName = magicBoltState.mSourceName;
|
2021-08-27 20:07:50 +02:00
|
|
|
cast.mSlot = magicBoltState.mSlot;
|
2022-08-21 12:44:07 +02:00
|
|
|
// Grab original effect list so the indices are correct
|
|
|
|
const ESM::EffectList* effects;
|
|
|
|
if (const ESM::Spell* spell = esmStore.get<ESM::Spell>().search(magicBoltState.mSpellId))
|
|
|
|
effects = &spell->mEffects;
|
|
|
|
else
|
2022-08-22 17:44:49 +02:00
|
|
|
{
|
|
|
|
MWWorld::ManualRef ref(esmStore, magicBoltState.mSpellId);
|
|
|
|
const MWWorld::Ptr& ptr = ref.getPtr();
|
|
|
|
effects = &esmStore.get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr))->mEffects;
|
|
|
|
}
|
2022-09-04 14:51:19 +02:00
|
|
|
cast.inflict(target, *effects, ESM::RT_Target);
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2021-08-05 10:55:19 +02:00
|
|
|
magicBoltState.mToDelete = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& projectileState : mProjectiles)
|
|
|
|
{
|
|
|
|
if (projectileState.mToDelete)
|
|
|
|
cleanupProjectile(projectileState);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& magicBoltState : mMagicBolts)
|
|
|
|
{
|
|
|
|
if (magicBoltState.mToDelete)
|
|
|
|
cleanupMagicBolt(magicBoltState);
|
2019-02-13 11:30:16 +04:00
|
|
|
}
|
2020-10-23 20:27:07 +02:00
|
|
|
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(),
|
|
|
|
[](const State& state) { return state.mToDelete; }),
|
|
|
|
mProjectiles.end());
|
|
|
|
mMagicBolts.erase(
|
|
|
|
std::remove_if(mMagicBolts.begin(), mMagicBolts.end(), [](const State& state) { return state.mToDelete; }),
|
|
|
|
mMagicBolts.end());
|
2019-02-13 11:30:16 +04:00
|
|
|
}
|
|
|
|
|
2017-09-23 18:54:17 +02:00
|
|
|
void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state)
|
|
|
|
{
|
|
|
|
mParent->removeChild(state.mNode);
|
2019-02-13 11:30:16 +04:00
|
|
|
mPhysics->removeProjectile(state.mProjectileId);
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = true;
|
2017-09-23 18:54:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state)
|
|
|
|
{
|
|
|
|
mParent->removeChild(state.mNode);
|
2019-02-13 11:30:16 +04:00
|
|
|
mPhysics->removeProjectile(state.mProjectileId);
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = true;
|
2017-09-23 18:54:17 +02:00
|
|
|
for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++)
|
|
|
|
{
|
|
|
|
MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
void ProjectileManager::clear()
|
|
|
|
{
|
2020-10-23 20:27:07 +02:00
|
|
|
for (auto& mProjectile : mProjectiles)
|
|
|
|
cleanupProjectile(mProjectile);
|
2014-05-16 13:09:23 +02:00
|
|
|
mProjectiles.clear();
|
2020-10-23 20:27:07 +02:00
|
|
|
|
|
|
|
for (auto& mMagicBolt : mMagicBolts)
|
|
|
|
cleanupMagicBolt(mMagicBolt);
|
2014-05-16 13:09:23 +02:00
|
|
|
mMagicBolts.clear();
|
|
|
|
}
|
|
|
|
|
2014-05-17 05:21:17 +02:00
|
|
|
void ProjectileManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) const
|
|
|
|
{
|
|
|
|
for (std::vector<ProjectileState>::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
|
|
|
|
{
|
|
|
|
writer.startRecord(ESM::REC_PROJ);
|
|
|
|
|
|
|
|
ESM::ProjectileState state;
|
2016-09-04 01:54:09 +09:00
|
|
|
state.mId = it->mIdArrow;
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition()));
|
|
|
|
state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude()));
|
2014-05-17 05:21:17 +02:00
|
|
|
state.mActorId = it->mActorId;
|
|
|
|
|
|
|
|
state.mBowId = it->mBowId;
|
|
|
|
state.mVelocity = it->mVelocity;
|
2015-06-26 02:32:41 +02:00
|
|
|
state.mAttackStrength = it->mAttackStrength;
|
2014-05-17 05:21:17 +02:00
|
|
|
|
|
|
|
state.save(writer);
|
|
|
|
|
|
|
|
writer.endRecord(ESM::REC_PROJ);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::vector<MagicBoltState>::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
|
|
|
|
{
|
|
|
|
writer.startRecord(ESM::REC_MPRJ);
|
|
|
|
|
|
|
|
ESM::MagicBoltState state;
|
2016-09-04 01:54:09 +09:00
|
|
|
state.mId = it->mIdMagic.at(0);
|
2015-06-01 21:41:13 +02:00
|
|
|
state.mPosition = ESM::Vector3(osg::Vec3f(it->mNode->getPosition()));
|
|
|
|
state.mOrientation = ESM::Quaternion(osg::Quat(it->mNode->getAttitude()));
|
2014-05-17 05:21:17 +02:00
|
|
|
state.mActorId = it->mActorId;
|
2021-08-27 20:07:50 +02:00
|
|
|
state.mSlot = it->mSlot;
|
2014-05-17 05:21:17 +02:00
|
|
|
state.mSpellId = it->mSpellId;
|
|
|
|
state.mSpeed = it->mSpeed;
|
|
|
|
|
|
|
|
state.save(writer);
|
|
|
|
|
|
|
|
writer.endRecord(ESM::REC_MPRJ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 19:04:59 +01:00
|
|
|
bool ProjectileManager::readRecord(ESM::ESMReader& reader, uint32_t type)
|
2014-05-17 05:21:17 +02:00
|
|
|
{
|
2016-12-03 19:09:03 -07:00
|
|
|
if (type == ESM::REC_PROJ)
|
2014-05-17 05:21:17 +02:00
|
|
|
{
|
2016-12-03 19:09:03 -07:00
|
|
|
ESM::ProjectileState esm;
|
2014-05-17 05:21:17 +02:00
|
|
|
esm.load(reader);
|
|
|
|
|
|
|
|
ProjectileState state;
|
|
|
|
state.mActorId = esm.mActorId;
|
|
|
|
state.mBowId = esm.mBowId;
|
|
|
|
state.mVelocity = esm.mVelocity;
|
2016-09-04 01:54:09 +09:00
|
|
|
state.mIdArrow = esm.mId;
|
2015-06-26 02:32:41 +02:00
|
|
|
state.mAttackStrength = esm.mAttackStrength;
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = false;
|
2014-05-17 05:21:17 +02:00
|
|
|
|
|
|
|
std::string model;
|
|
|
|
try
|
|
|
|
{
|
2023-04-20 21:07:53 +02:00
|
|
|
MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), esm.mId);
|
2014-05-17 05:21:17 +02:00
|
|
|
MWWorld::Ptr ptr = ref.getPtr();
|
|
|
|
model = ptr.getClass().getModel(ptr);
|
2018-12-26 13:45:28 +04:00
|
|
|
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
|
|
|
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
2019-02-13 11:30:16 +04:00
|
|
|
|
2021-08-31 16:25:45 +02:00
|
|
|
state.mProjectileId
|
|
|
|
= mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false);
|
2014-05-17 05:21:17 +02:00
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-12-13 18:04:20 -07:00
|
|
|
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), false, false,
|
|
|
|
osg::Vec4(0, 0, 0, 0));
|
2014-05-17 05:21:17 +02:00
|
|
|
|
|
|
|
mProjectiles.push_back(state);
|
|
|
|
return true;
|
|
|
|
}
|
2020-10-23 20:27:07 +02:00
|
|
|
if (type == ESM::REC_MPRJ)
|
2014-05-17 05:21:17 +02:00
|
|
|
{
|
2016-12-03 19:09:03 -07:00
|
|
|
ESM::MagicBoltState esm;
|
2014-05-17 05:21:17 +02:00
|
|
|
esm.load(reader);
|
|
|
|
|
|
|
|
MagicBoltState state;
|
2016-09-04 01:54:09 +09:00
|
|
|
state.mIdMagic.push_back(esm.mId);
|
2014-05-17 05:21:17 +02:00
|
|
|
state.mSpellId = esm.mSpellId;
|
|
|
|
state.mActorId = esm.mActorId;
|
2020-10-23 20:27:07 +02:00
|
|
|
state.mToDelete = false;
|
2021-08-27 20:07:50 +02:00
|
|
|
state.mSlot = esm.mSlot;
|
2020-10-23 20:27:07 +02:00
|
|
|
std::string texture;
|
2017-09-20 18:56:32 +02:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
state.mEffects = getMagicBoltData(
|
|
|
|
state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Warning: Failed to recreate magic projectile from saved data (id \""
|
|
|
|
<< state.mSpellId << "\" no longer exists?)";
|
2017-09-20 18:56:32 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-05 05:41:24 +09:00
|
|
|
state.mSpeed = esm.mSpeed; // speed is derived from non-projectile effects as well as
|
|
|
|
// projectile effects, so we can't calculate it from the save
|
|
|
|
// file's effect list, which is already trimmed of non-projectile
|
|
|
|
// effects. We need to use the stored value.
|
2016-09-05 03:31:48 +09:00
|
|
|
|
2014-05-17 05:21:17 +02:00
|
|
|
std::string model;
|
|
|
|
try
|
|
|
|
{
|
2023-04-20 21:07:53 +02:00
|
|
|
MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), state.mIdMagic.at(0));
|
2014-05-17 05:21:17 +02:00
|
|
|
MWWorld::Ptr ptr = ref.getPtr();
|
|
|
|
model = ptr.getClass().getModel(ptr);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-20 18:56:32 +02:00
|
|
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
2021-02-05 12:12:34 +01:00
|
|
|
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true,
|
|
|
|
lightDiffuseColor, texture);
|
2021-08-31 16:25:45 +02:00
|
|
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true);
|
2014-05-17 05:21:17 +02:00
|
|
|
|
|
|
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
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& soundid : state.mSoundIds)
|
2016-09-05 03:31:48 +09:00
|
|
|
{
|
2019-04-17 19:23:50 +03:00
|
|
|
MWBase::Sound* sound = sndMgr->playSound3D(
|
2017-09-15 01:03:41 -07:00
|
|
|
esm.mPosition, soundid, 1.0f, 1.0f, MWSound::Type::Sfx, MWSound::PlayMode::Loop);
|
2017-01-01 21:34:31 +01:00
|
|
|
if (sound)
|
|
|
|
state.mSounds.push_back(sound);
|
2016-09-05 03:31:48 +09:00
|
|
|
}
|
2014-05-17 05:21:17 +02:00
|
|
|
|
|
|
|
mMagicBolts.push_back(state);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ProjectileManager::countSavedGameRecords() const
|
|
|
|
{
|
|
|
|
return mMagicBolts.size() + mProjectiles.size();
|
|
|
|
}
|
|
|
|
|
2015-06-01 21:41:13 +02:00
|
|
|
MWWorld::Ptr ProjectileManager::State::getCaster()
|
|
|
|
{
|
|
|
|
if (!mCasterHandle.isEmpty())
|
|
|
|
return mCasterHandle;
|
|
|
|
|
|
|
|
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId);
|
|
|
|
}
|
|
|
|
|
2014-05-16 13:09:23 +02:00
|
|
|
}
|