2010-08-03 13:03:08 +02:00
|
|
|
#include "creature.hpp"
|
|
|
|
|
2022-10-09 10:39:43 +00:00
|
|
|
#include <MyGUI_TextIterator.h>
|
|
|
|
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/creaturestate.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadclas.hpp>
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/loadcrea.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadsndg.hpp>
|
|
|
|
#include <components/esm3/loadsoun.hpp>
|
2015-04-22 17:58:55 +02:00
|
|
|
#include <components/misc/rng.hpp>
|
2017-07-26 14:42:01 +04:00
|
|
|
#include <components/settings/settings.hpp>
|
2010-08-03 13:03:08 +02:00
|
|
|
|
2022-06-26 16:42:29 +02:00
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
|
|
|
#include "../mwmechanics/aisetting.hpp"
|
|
|
|
#include "../mwmechanics/combat.hpp"
|
|
|
|
#include "../mwmechanics/creaturecustomdataresetter.hpp"
|
2010-08-03 13:03:08 +02:00
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
2014-07-20 16:22:52 +02:00
|
|
|
#include "../mwmechanics/difficultyscaling.hpp"
|
2014-03-08 05:51:47 +01:00
|
|
|
#include "../mwmechanics/disease.hpp"
|
2022-06-26 16:42:29 +02:00
|
|
|
#include "../mwmechanics/inventory.hpp"
|
2012-05-15 21:34:00 +02:00
|
|
|
#include "../mwmechanics/magiceffects.hpp"
|
2013-05-27 18:03:06 +02:00
|
|
|
#include "../mwmechanics/movement.hpp"
|
2022-06-26 16:42:29 +02:00
|
|
|
#include "../mwmechanics/npcstats.hpp"
|
2022-07-16 16:37:31 +02:00
|
|
|
#include "../mwmechanics/setbaseaisetting.hpp"
|
2010-08-03 13:03:08 +02:00
|
|
|
|
2012-04-23 15:27:03 +02:00
|
|
|
#include "../mwbase/environment.hpp"
|
2012-08-11 17:30:55 +02:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2013-07-26 05:24:56 -07:00
|
|
|
#include "../mwbase/soundmanager.hpp"
|
2012-08-12 18:11:09 +02:00
|
|
|
#include "../mwbase/windowmanager.hpp"
|
2013-04-03 23:55:57 +02:00
|
|
|
#include "../mwbase/world.hpp"
|
2012-04-23 15:27:03 +02:00
|
|
|
|
2012-10-28 16:04:33 +01:00
|
|
|
#include "../mwworld/actionopen.hpp"
|
2013-08-08 22:34:53 -07:00
|
|
|
#include "../mwworld/actiontalk.hpp"
|
2014-02-23 20:11:05 +01:00
|
|
|
#include "../mwworld/cellstore.hpp"
|
2022-06-26 16:42:29 +02:00
|
|
|
#include "../mwworld/containerstore.hpp"
|
|
|
|
#include "../mwworld/customdata.hpp"
|
|
|
|
#include "../mwworld/esmstore.hpp"
|
2012-10-28 16:04:33 +01:00
|
|
|
#include "../mwworld/failedaction.hpp"
|
2012-01-27 14:55:58 +01:00
|
|
|
#include "../mwworld/inventorystore.hpp"
|
2019-08-06 21:30:21 +02:00
|
|
|
#include "../mwworld/localscripts.hpp"
|
2010-08-03 13:17:31 +02:00
|
|
|
#include "../mwworld/ptr.hpp"
|
2012-07-03 13:15:20 +02:00
|
|
|
|
2015-04-12 15:34:50 +02:00
|
|
|
#include "../mwrender/objects.hpp"
|
2012-07-03 13:15:20 +02:00
|
|
|
#include "../mwrender/renderinginterface.hpp"
|
2010-08-03 13:03:08 +02:00
|
|
|
|
2012-07-03 13:15:20 +02:00
|
|
|
#include "../mwgui/tooltips.hpp"
|
2022-08-24 22:16:03 +02:00
|
|
|
#include "../mwgui/ustring.hpp"
|
2012-04-16 22:58:16 +02:00
|
|
|
|
2022-06-29 00:32:11 +02:00
|
|
|
#include "classmodel.hpp"
|
|
|
|
|
2012-01-26 11:35:47 +01:00
|
|
|
namespace
|
|
|
|
{
|
2015-12-18 16:15:40 +01:00
|
|
|
bool isFlagBitSet(const MWWorld::ConstPtr& ptr, ESM::Creature::Flags bitMask)
|
2015-11-29 14:13:14 +01:00
|
|
|
{
|
|
|
|
return (ptr.get<ESM::Creature>()->mBase->mFlags & bitMask) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace MWClass
|
|
|
|
{
|
|
|
|
|
2021-04-03 12:59:44 +02:00
|
|
|
class CreatureCustomData : public MWWorld::TypedCustomData<CreatureCustomData>
|
2012-01-26 11:35:47 +01:00
|
|
|
{
|
2015-11-29 14:13:14 +01:00
|
|
|
public:
|
2012-01-26 11:35:47 +01:00
|
|
|
MWMechanics::CreatureStats mCreatureStats;
|
2021-04-03 00:48:35 +02:00
|
|
|
std::unique_ptr<MWWorld::ContainerStore> mContainerStore; // may be InventoryStore for some creatures
|
2013-05-27 18:03:06 +02:00
|
|
|
MWMechanics::Movement mMovement;
|
2012-01-26 11:35:47 +01:00
|
|
|
|
2021-04-03 00:48:35 +02:00
|
|
|
CreatureCustomData() = default;
|
|
|
|
CreatureCustomData(const CreatureCustomData& other);
|
2021-04-24 00:16:57 +02:00
|
|
|
CreatureCustomData(CreatureCustomData&& other) = default;
|
2021-04-03 00:48:35 +02:00
|
|
|
|
2020-10-16 22:18:54 +04:00
|
|
|
CreatureCustomData& asCreatureCustomData() override { return *this; }
|
|
|
|
const CreatureCustomData& asCreatureCustomData() const override { return *this; }
|
2012-01-26 11:35:47 +01:00
|
|
|
};
|
|
|
|
|
2021-04-03 00:48:35 +02:00
|
|
|
CreatureCustomData::CreatureCustomData(const CreatureCustomData& other)
|
|
|
|
: mCreatureStats(other.mCreatureStats)
|
|
|
|
, mContainerStore(other.mContainerStore->clone())
|
|
|
|
, mMovement(other.mMovement)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-04-04 02:44:53 +02:00
|
|
|
Creature::Creature()
|
|
|
|
: MWWorld::RegisteredClass<Creature, Actor>(ESM::Creature::sRecordId)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-06-19 02:09:46 +02:00
|
|
|
const Creature::GMST& Creature::getGmst()
|
|
|
|
{
|
2021-10-24 18:45:04 +04:00
|
|
|
static const GMST staticGmst = [] {
|
2020-06-14 17:43:30 +02:00
|
|
|
GMST gmst;
|
|
|
|
|
2023-04-20 13:37:01 +02:00
|
|
|
const MWWorld::Store<ESM::GameSetting>& store
|
2023-04-20 21:07:53 +02:00
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
2020-06-14 17:43:30 +02:00
|
|
|
|
2014-06-19 02:09:46 +02:00
|
|
|
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
|
|
|
|
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
|
|
|
|
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
|
|
|
|
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
|
|
|
|
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
|
|
|
|
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
|
|
|
|
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
|
|
|
|
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
|
|
|
|
gmst.fSwimRunBase = store.find("fSwimRunBase");
|
|
|
|
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
|
|
|
|
gmst.fKnockDownMult = store.find("fKnockDownMult");
|
|
|
|
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
|
|
|
|
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
|
2020-06-14 17:43:30 +02:00
|
|
|
|
|
|
|
return gmst;
|
|
|
|
}();
|
2021-10-24 18:45:04 +04:00
|
|
|
return staticGmst;
|
2014-06-19 02:09:46 +02:00
|
|
|
}
|
|
|
|
|
2012-01-26 11:35:47 +01:00
|
|
|
void Creature::ensureCustomData(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
if (!ptr.getRefData().getCustomData())
|
|
|
|
{
|
2021-10-25 16:54:50 +02:00
|
|
|
auto tempData = std::make_unique<CreatureCustomData>();
|
|
|
|
CreatureCustomData* data = tempData.get();
|
2022-06-26 16:42:29 +02:00
|
|
|
MWMechanics::CreatureCustomDataResetter resetter{ ptr };
|
2021-10-25 16:54:50 +02:00
|
|
|
ptr.getRefData().setCustomData(std::move(tempData));
|
2012-01-26 11:35:47 +01:00
|
|
|
|
2012-06-29 18:54:23 +02:00
|
|
|
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2012-01-26 11:35:47 +01:00
|
|
|
|
|
|
|
// creature stats
|
2013-12-28 17:19:35 +01:00
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mData.mStrength);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mData.mIntelligence);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mData.mWillpower);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mData.mAgility);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mData.mSpeed);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mData.mEndurance);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mData.mPersonality);
|
|
|
|
data->mCreatureStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mData.mLuck);
|
2015-03-08 13:07:29 +13:00
|
|
|
data->mCreatureStats.setHealth(static_cast<float>(ref->mBase->mData.mHealth));
|
|
|
|
data->mCreatureStats.setMagicka(static_cast<float>(ref->mBase->mData.mMana));
|
|
|
|
data->mCreatureStats.setFatigue(static_cast<float>(ref->mBase->mData.mFatigue));
|
2012-11-05 16:07:59 +04:00
|
|
|
|
|
|
|
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);
|
|
|
|
|
2013-07-31 00:55:08 +02:00
|
|
|
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
|
|
|
|
|
2022-07-16 16:37:31 +02:00
|
|
|
data->mCreatureStats.setAiSetting(MWMechanics::AiSetting::Hello, ref->mBase->mAiData.mHello);
|
|
|
|
data->mCreatureStats.setAiSetting(MWMechanics::AiSetting::Fight, ref->mBase->mAiData.mFight);
|
|
|
|
data->mCreatureStats.setAiSetting(MWMechanics::AiSetting::Flee, ref->mBase->mAiData.mFlee);
|
|
|
|
data->mCreatureStats.setAiSetting(MWMechanics::AiSetting::Alarm, ref->mBase->mAiData.mAlarm);
|
2012-09-10 13:04:00 +02:00
|
|
|
|
2018-06-12 10:00:38 +04:00
|
|
|
// Persistent actors with 0 health do not play death animation
|
2016-06-12 00:04:50 +02:00
|
|
|
if (data->mCreatureStats.isDead())
|
2018-10-28 17:03:38 +03:00
|
|
|
data->mCreatureStats.setDeathAnimationFinished(isPersistent(ptr));
|
2016-06-12 00:04:50 +02:00
|
|
|
|
2012-09-10 13:04:00 +02:00
|
|
|
// spells
|
2020-07-28 08:33:28 +02:00
|
|
|
bool spellsInitialised = data->mCreatureStats.getSpells().setSpells(ref->mBase->mId);
|
|
|
|
if (!spellsInitialised)
|
|
|
|
data->mCreatureStats.getSpells().addAllToInstance(ref->mBase->mSpells.mList);
|
2012-06-16 16:17:42 +02:00
|
|
|
|
2013-07-31 00:02:24 +02:00
|
|
|
// inventory
|
2015-08-27 18:36:46 +12:00
|
|
|
bool hasInventory = hasInventoryStore(ptr);
|
|
|
|
if (hasInventory)
|
2021-04-03 00:48:35 +02:00
|
|
|
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
|
2014-01-19 11:42:58 +01:00
|
|
|
else
|
2021-04-03 00:48:35 +02:00
|
|
|
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
|
2014-01-19 11:42:58 +01:00
|
|
|
|
2014-03-27 01:23:56 -04:00
|
|
|
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
|
|
|
|
|
2021-10-25 16:54:50 +02:00
|
|
|
resetter.mPtr = {};
|
2014-01-19 11:42:58 +01:00
|
|
|
|
2022-03-06 21:56:02 +02:00
|
|
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
|
|
|
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
|
2013-07-31 00:02:24 +02:00
|
|
|
|
2015-08-27 18:36:46 +12:00
|
|
|
if (hasInventory)
|
2023-01-16 23:51:04 +01:00
|
|
|
getInventoryStore(ptr).autoEquip();
|
2012-01-26 11:35:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-12 11:29:56 +01:00
|
|
|
void Creature::insertObjectRendering(
|
|
|
|
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
|
2010-08-14 10:02:54 +02:00
|
|
|
{
|
2015-04-12 15:34:50 +02:00
|
|
|
MWRender::Objects& objects = renderingInterface.getObjects();
|
2015-08-27 18:36:46 +12:00
|
|
|
objects.insertCreature(ptr, model, hasInventoryStore(ptr));
|
2010-08-14 10:02:54 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 15:51:05 +01:00
|
|
|
std::string Creature::getModel(const MWWorld::ConstPtr& ptr) const
|
2011-11-11 23:01:12 -05:00
|
|
|
{
|
2022-06-29 00:32:11 +02:00
|
|
|
return getClassModel<ESM::Creature>(ptr);
|
2010-08-14 11:27:13 +02:00
|
|
|
}
|
|
|
|
|
2016-02-08 20:52:32 +01:00
|
|
|
void Creature::getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const
|
|
|
|
{
|
|
|
|
std::string model = getModel(ptr);
|
|
|
|
if (!model.empty())
|
|
|
|
models.push_back(model);
|
|
|
|
|
|
|
|
// FIXME: use const version of InventoryStore functions once they are available
|
2018-10-28 17:03:38 +03:00
|
|
|
if (hasInventoryStore(ptr))
|
2016-02-08 20:52:32 +01:00
|
|
|
{
|
2018-10-28 17:03:38 +03:00
|
|
|
const MWWorld::InventoryStore& invStore = getInventoryStore(ptr);
|
2016-02-08 20:52:32 +01:00
|
|
|
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
|
|
|
{
|
2017-02-26 21:24:51 +00:00
|
|
|
MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot);
|
2016-02-08 20:52:32 +01:00
|
|
|
if (equipped != invStore.end())
|
|
|
|
{
|
|
|
|
model = equipped->getClass().getModel(*equipped);
|
|
|
|
if (!model.empty())
|
|
|
|
models.push_back(model);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-16 21:15:03 +02:00
|
|
|
std::string_view Creature::getName(const MWWorld::ConstPtr& ptr) const
|
2010-08-03 17:11:41 +02:00
|
|
|
{
|
2015-12-18 15:27:06 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2019-09-11 00:06:50 +03:00
|
|
|
const std::string& name = ref->mBase->mName;
|
2010-08-03 17:11:41 +02:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
return !name.empty() ? name : ref->mBase->mId.getRefIdString();
|
2010-08-03 17:11:41 +02:00
|
|
|
}
|
|
|
|
|
2010-08-03 13:17:31 +02:00
|
|
|
MWMechanics::CreatureStats& Creature::getCreatureStats(const MWWorld::Ptr& ptr) const
|
2010-08-03 13:03:08 +02:00
|
|
|
{
|
2012-01-26 11:35:47 +01:00
|
|
|
ensureCustomData(ptr);
|
2010-08-03 13:03:08 +02:00
|
|
|
|
2015-11-29 14:13:14 +01:00
|
|
|
return ptr.getRefData().getCustomData()->asCreatureCustomData().mCreatureStats;
|
2010-08-03 13:03:08 +02:00
|
|
|
}
|
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
bool Creature::evaluateHit(const MWWorld::Ptr& ptr, MWWorld::Ptr& victim, osg::Vec3f& hitPosition) const
|
2013-07-24 02:51:42 -07:00
|
|
|
{
|
2022-08-11 21:50:38 +03:00
|
|
|
victim = MWWorld::Ptr();
|
|
|
|
hitPosition = osg::Vec3f();
|
2014-09-15 00:29:21 +02:00
|
|
|
|
2014-01-19 16:13:16 +01:00
|
|
|
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
|
|
|
MWWorld::Ptr weapon;
|
2018-10-28 17:03:38 +03:00
|
|
|
if (hasInventoryStore(ptr))
|
2014-01-19 16:13:16 +01:00
|
|
|
{
|
|
|
|
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
|
|
|
|
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
2021-10-11 11:46:21 +00:00
|
|
|
if (weaponslot != inv.end() && weaponslot->getType() == ESM::Weapon::sRecordId)
|
2014-01-19 16:13:16 +01:00
|
|
|
weapon = *weaponslot;
|
|
|
|
}
|
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
|
|
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
|
|
|
|
float dist = store.find("fCombatDistance")->mValue.getFloat();
|
2014-01-19 16:13:16 +01:00
|
|
|
if (!weapon.isEmpty())
|
2016-06-10 23:29:41 +02:00
|
|
|
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
|
|
|
|
|
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;
|
2022-08-11 21:50:38 +03:00
|
|
|
getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
|
2017-02-02 16:20:34 +09:00
|
|
|
|
|
|
|
std::pair<MWWorld::Ptr, osg::Vec3f> result
|
|
|
|
= MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
2022-08-11 21:50:38 +03:00
|
|
|
if (result.first.isEmpty()) // Didn't hit anything
|
|
|
|
return true;
|
2014-01-17 17:19:08 +01:00
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
const MWWorld::Class& othercls = result.first.getClass();
|
|
|
|
if (!othercls.isActor()) // Can't hit non-actors
|
|
|
|
return true;
|
2014-01-17 17:19:08 +01:00
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
MWMechanics::CreatureStats& otherstats = othercls.getCreatureStats(result.first);
|
|
|
|
if (otherstats.isDead()) // Can't hit dead actors
|
|
|
|
return true;
|
2014-01-17 17:19:08 +01:00
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
// Note that earlier we returned true in spite of an apparent failure to hit anything alive.
|
|
|
|
// This is because hitting nothing is not a "miss" and should be handled as such character controller-side.
|
|
|
|
victim = result.first;
|
|
|
|
hitPosition = result.second;
|
|
|
|
|
|
|
|
float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.get<ESM::Creature>()->mBase->mData.mCombat);
|
|
|
|
return Misc::Rng::roll0to99(world->getPrng()) < hitchance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type, const MWWorld::Ptr& victim,
|
|
|
|
const osg::Vec3f& hitPosition, bool success) const
|
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
|
|
|
|
|
|
|
if (stats.getDrawState() != MWMechanics::DrawState::Weapon)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MWWorld::Ptr weapon;
|
|
|
|
if (hasInventoryStore(ptr))
|
|
|
|
{
|
|
|
|
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
|
|
|
|
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
|
|
if (weaponslot != inv.end() && weaponslot->getType() == ESM::Weapon::sRecordId)
|
|
|
|
weapon = *weaponslot;
|
|
|
|
}
|
|
|
|
|
|
|
|
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
|
|
|
|
|
|
|
|
if (victim.isEmpty())
|
|
|
|
return; // Didn't hit anything
|
|
|
|
|
|
|
|
const MWWorld::Class& othercls = victim.getClass();
|
|
|
|
MWMechanics::CreatureStats& otherstats = othercls.getCreatureStats(victim);
|
|
|
|
if (otherstats.isDead()) // Can't hit dead actors
|
|
|
|
return;
|
2014-01-17 17:19:08 +01:00
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
if (!success)
|
2014-01-17 17:19:08 +01:00
|
|
|
{
|
2016-09-12 20:40:40 +09:00
|
|
|
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
2014-08-02 23:14:17 +02:00
|
|
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
2014-01-17 17:19:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-11 21:50:38 +03:00
|
|
|
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2014-01-17 17:19:08 +01:00
|
|
|
int min, max;
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
min = ref->mBase->mData.mAttack[0];
|
|
|
|
max = ref->mBase->mData.mAttack[1];
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
min = ref->mBase->mData.mAttack[2];
|
|
|
|
max = ref->mBase->mData.mAttack[3];
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
min = ref->mBase->mData.mAttack[4];
|
|
|
|
max = ref->mBase->mData.mAttack[5];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-26 05:15:07 +02:00
|
|
|
float damage = min + (max - min) * attackStrength;
|
2014-12-31 23:13:36 +01:00
|
|
|
bool healthdmg = true;
|
2014-01-19 16:13:16 +01:00
|
|
|
if (!weapon.isEmpty())
|
|
|
|
{
|
2018-10-09 10:21:12 +04:00
|
|
|
const unsigned char* attack = nullptr;
|
2014-01-28 21:07:26 +02:00
|
|
|
if (type == ESM::Weapon::AT_Chop)
|
2014-01-19 16:13:16 +01:00
|
|
|
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
2014-01-28 21:07:26 +02:00
|
|
|
else if (type == ESM::Weapon::AT_Slash)
|
2014-01-19 16:13:16 +01:00
|
|
|
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
|
2014-01-28 21:07:26 +02:00
|
|
|
else if (type == ESM::Weapon::AT_Thrust)
|
2014-01-19 16:13:16 +01:00
|
|
|
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
|
|
|
|
if (attack)
|
|
|
|
{
|
2015-06-26 05:15:07 +02:00
|
|
|
damage = attack[0] + ((attack[1] - attack[0]) * attackStrength);
|
2015-03-12 03:08:58 +01:00
|
|
|
MWMechanics::adjustWeaponDamage(damage, weapon, ptr);
|
2014-08-02 23:14:17 +02:00
|
|
|
MWMechanics::reduceWeaponCondition(damage, true, weapon, ptr);
|
2022-02-23 10:10:50 +03:00
|
|
|
MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);
|
2014-01-19 16:13:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply "On hit" enchanted weapons
|
2016-02-22 19:37:19 +01:00
|
|
|
MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
|
2014-01-19 16:13:16 +01:00
|
|
|
}
|
2014-12-31 23:13:36 +01:00
|
|
|
else if (isBipedal(ptr))
|
|
|
|
{
|
2015-06-26 05:15:07 +02:00
|
|
|
MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg, attackStrength);
|
2014-12-31 23:13:36 +01:00
|
|
|
}
|
2014-01-19 16:13:16 +01:00
|
|
|
|
2014-07-15 21:53:11 +02:00
|
|
|
MWMechanics::applyElementalShields(ptr, victim);
|
|
|
|
|
2015-06-26 05:15:07 +02:00
|
|
|
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
|
2022-11-07 21:52:34 +03:00
|
|
|
{
|
2014-01-21 01:01:21 +01:00
|
|
|
damage = 0;
|
2022-12-26 18:52:18 +03:00
|
|
|
victim.getClass().block(victim);
|
2022-11-07 21:52:34 +03:00
|
|
|
}
|
2014-01-21 01:01:21 +01:00
|
|
|
|
2014-03-08 05:51:47 +01:00
|
|
|
MWMechanics::diseaseContact(victim, ptr);
|
|
|
|
|
2016-09-12 20:40:40 +09:00
|
|
|
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
2013-07-24 02:51:42 -07:00
|
|
|
}
|
|
|
|
|
2016-09-13 00:49:31 +09:00
|
|
|
void Creature::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
|
|
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
|
2013-07-24 02:51:42 -07:00
|
|
|
{
|
2018-10-28 17:03:38 +03:00
|
|
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
2013-07-24 02:51:42 -07:00
|
|
|
|
2017-02-02 02:15:10 +09:00
|
|
|
// NOTE: 'object' and/or 'attacker' may be empty.
|
2017-02-11 19:59:42 +09:00
|
|
|
if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker))
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setAttacked(true);
|
2014-05-06 18:23:17 +02:00
|
|
|
|
|
|
|
// Self defense
|
2014-09-26 22:08:07 +02:00
|
|
|
bool setOnPcHitMe = true; // Note OnPcHitMe is not set for friendly hits.
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2015-01-09 21:40:53 +13:00
|
|
|
// No retaliation for totally static creatures (they have no movement or attacks anyway)
|
|
|
|
if (isMobile(ptr) && !attacker.isEmpty())
|
2014-09-26 22:08:07 +02:00
|
|
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
2017-02-02 02:15:10 +09:00
|
|
|
|
|
|
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
2018-10-28 17:03:38 +03:00
|
|
|
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
2017-02-02 02:15:10 +09:00
|
|
|
{
|
|
|
|
MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker);
|
|
|
|
// First handle the attacked actor
|
2017-02-06 21:32:36 +09:00
|
|
|
if ((stats.getHitAttemptActorId() == -1)
|
2017-02-02 02:15:10 +09:00
|
|
|
&& (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer()))
|
2017-02-06 21:32:36 +09:00
|
|
|
stats.setHitAttemptActorId(statsAttacker.getActorId());
|
2017-02-02 02:15:10 +09:00
|
|
|
|
|
|
|
// Next handle the attacking actor
|
2017-02-06 21:32:36 +09:00
|
|
|
if ((statsAttacker.getHitAttemptActorId() == -1)
|
2017-02-02 02:15:10 +09:00
|
|
|
&& (statsAttacker.getAiSequence().isInCombat(ptr) || attacker == MWMechanics::getPlayer()))
|
2017-02-06 21:32:36 +09:00
|
|
|
statsAttacker.setHitAttemptActorId(stats.getActorId());
|
2014-07-27 20:30:52 +02:00
|
|
|
}
|
2014-05-06 18:23:17 +02:00
|
|
|
|
2016-09-12 20:40:40 +09:00
|
|
|
if (!object.isEmpty())
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
2014-12-11 22:25:41 +01:00
|
|
|
|
2016-09-12 20:40:40 +09:00
|
|
|
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
2015-06-18 15:00:04 +02:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& script = ptr.get<ESM::Creature>()->mBase->mScript;
|
2015-06-18 15:00:04 +02:00
|
|
|
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
|
|
|
if (!script.empty())
|
|
|
|
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
|
|
|
|
}
|
|
|
|
|
2016-09-12 20:40:40 +09:00
|
|
|
if (!successful)
|
2013-07-24 02:51:42 -07:00
|
|
|
{
|
2013-07-26 05:24:56 -07:00
|
|
|
// Missed
|
2019-04-10 22:20:56 +03:00
|
|
|
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
2022-10-18 09:26:55 +02:00
|
|
|
MWBase::Environment::get().getSoundManager()->playSound3D(
|
|
|
|
ptr, ESM::RefId::stringRefId("miss"), 1.0f, 1.0f);
|
2013-07-26 05:24:56 -07:00
|
|
|
return;
|
2013-07-24 02:51:42 -07:00
|
|
|
}
|
|
|
|
|
2016-09-12 20:40:40 +09:00
|
|
|
if (!object.isEmpty())
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setLastHitObject(object.getCellRef().getRefId());
|
2013-07-26 07:04:36 -07:00
|
|
|
|
2014-07-20 23:08:22 +02:00
|
|
|
if (damage < 0.001f)
|
|
|
|
damage = 0;
|
|
|
|
|
2014-01-21 01:01:21 +01:00
|
|
|
if (damage > 0.f)
|
2014-01-17 17:40:58 +01:00
|
|
|
{
|
2014-08-07 19:30:04 +02:00
|
|
|
if (!attacker.isEmpty())
|
2014-01-21 01:01:21 +01:00
|
|
|
{
|
2014-08-07 19:30:04 +02:00
|
|
|
// Check for knockdown
|
2018-08-29 18:38:12 +03:00
|
|
|
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
|
|
|
* getGmst().fKnockDownMult->mValue.getFloat();
|
2017-02-02 02:15:10 +09:00
|
|
|
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
2018-08-29 18:38:12 +03:00
|
|
|
* getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f
|
|
|
|
+ getGmst().iKnockDownOddsBase->mValue.getInteger();
|
2022-03-06 21:56:02 +02:00
|
|
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
|
|
|
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setKnockedDown(true);
|
2014-08-07 19:30:04 +02:00
|
|
|
else
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setHitRecovery(true); // Is this supposed to always occur?
|
2014-01-21 01:01:21 +01:00
|
|
|
}
|
2014-01-17 17:40:58 +01:00
|
|
|
|
2014-01-21 01:01:21 +01:00
|
|
|
if (ishealth)
|
|
|
|
{
|
2019-05-11 19:20:23 +03:00
|
|
|
damage *= damage / (damage + getArmorRating(ptr));
|
|
|
|
damage = std::max(1.f, damage);
|
2018-08-28 16:42:15 +03:00
|
|
|
if (!attacker.isEmpty())
|
2016-09-12 20:40:40 +09:00
|
|
|
{
|
2014-07-20 16:22:52 +02:00
|
|
|
damage = scaleDamage(damage, attacker, ptr);
|
2016-09-12 20:40:40 +09:00
|
|
|
MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);
|
|
|
|
}
|
2014-07-20 16:22:52 +02:00
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
MWBase::Environment::get().getSoundManager()->playSound3D(
|
|
|
|
ptr, ESM::RefId::stringRefId("Health Damage"), 1.0f, 1.0f);
|
2015-07-24 20:23:27 +02:00
|
|
|
|
2017-02-02 02:15:10 +09:00
|
|
|
MWMechanics::DynamicStat<float> health(stats.getHealth());
|
2015-07-24 20:23:27 +02:00
|
|
|
health.setCurrent(health.getCurrent() - damage);
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setHealth(health);
|
2014-01-21 01:01:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-02 02:15:10 +09:00
|
|
|
MWMechanics::DynamicStat<float> fatigue(stats.getFatigue());
|
2014-01-21 01:01:21 +01:00
|
|
|
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
2017-02-02 02:15:10 +09:00
|
|
|
stats.setFatigue(fatigue);
|
2014-01-21 01:01:21 +01:00
|
|
|
}
|
2013-07-28 06:48:25 -07:00
|
|
|
}
|
2014-01-21 01:01:21 +01:00
|
|
|
}
|
|
|
|
|
2022-04-06 17:06:55 +02:00
|
|
|
std::unique_ptr<MWWorld::Action> Creature::activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const
|
2010-08-06 18:15:46 +02:00
|
|
|
{
|
2014-05-22 20:37:22 +02:00
|
|
|
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
2013-08-08 22:34:53 -07:00
|
|
|
{
|
2023-04-20 21:07:53 +02:00
|
|
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
2022-03-06 21:56:02 +02:00
|
|
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
2022-10-12 19:51:42 +02:00
|
|
|
const ESM::Sound* sound = store.get<ESM::Sound>().searchRandom("WolfCreature", prng);
|
2013-08-11 00:35:19 -07:00
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
std::unique_ptr<MWWorld::Action> action = std::make_unique<MWWorld::FailedAction>("#{sWerewolfRefusal}");
|
2013-08-11 00:35:19 -07:00
|
|
|
if (sound)
|
|
|
|
action->setSound(sound->mId);
|
|
|
|
|
2013-08-08 22:34:53 -07:00
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2017-07-26 14:42:01 +04:00
|
|
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
|
|
|
|
|
|
|
if (stats.isDead())
|
|
|
|
{
|
|
|
|
bool canLoot = Settings::Manager::getBool("can loot during death animation", "Game");
|
|
|
|
|
|
|
|
// by default user can loot friendly actors during death animation
|
|
|
|
if (canLoot && !stats.getAiSequence().isInCombat())
|
2022-05-29 13:24:48 +02:00
|
|
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
2017-07-26 14:42:01 +04:00
|
|
|
|
|
|
|
// otherwise wait until death animation
|
|
|
|
if (stats.isDeathAnimationFinished())
|
2022-05-29 13:24:48 +02:00
|
|
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
2017-07-26 14:42:01 +04:00
|
|
|
}
|
2019-09-15 23:17:36 +03:00
|
|
|
else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown())
|
2022-05-29 13:24:48 +02:00
|
|
|
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
2017-07-26 14:42:01 +04:00
|
|
|
|
2019-09-15 23:17:36 +03:00
|
|
|
// Tribunal and some mod companions oddly enough must use open action as fallback
|
|
|
|
if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
|
2022-05-29 13:24:48 +02:00
|
|
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
2017-09-30 21:20:38 +04:00
|
|
|
|
2022-05-29 13:24:48 +02:00
|
|
|
return std::make_unique<MWWorld::FailedAction>();
|
2011-01-05 22:18:21 +01:00
|
|
|
}
|
2010-08-07 20:33:07 +02:00
|
|
|
|
2014-01-19 11:42:58 +01:00
|
|
|
MWWorld::ContainerStore& Creature::getContainerStore(const MWWorld::Ptr& ptr) const
|
2010-08-04 14:37:23 +02:00
|
|
|
{
|
2012-01-27 14:55:58 +01:00
|
|
|
ensureCustomData(ptr);
|
2023-01-16 23:51:04 +01:00
|
|
|
auto& store = *ptr.getRefData().getCustomData()->asCreatureCustomData().mContainerStore;
|
|
|
|
if (hasInventoryStore(ptr))
|
|
|
|
static_cast<MWWorld::InventoryStore&>(store).setActor(ptr);
|
|
|
|
return store;
|
2014-01-19 11:42:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
if (hasInventoryStore(ptr))
|
2023-01-16 23:51:04 +01:00
|
|
|
return static_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));
|
2014-01-19 11:42:58 +01:00
|
|
|
else
|
|
|
|
throw std::runtime_error("this creature has no inventory store");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Creature::hasInventoryStore(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, ESM::Creature::Weapon);
|
2011-01-05 22:18:21 +01:00
|
|
|
}
|
2010-08-06 19:20:05 +02:00
|
|
|
|
2023-02-20 23:18:05 +01:00
|
|
|
ESM::RefId Creature::getScript(const MWWorld::ConstPtr& ptr) const
|
2010-08-05 15:40:03 +02:00
|
|
|
{
|
2015-12-18 00:12:03 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2010-08-05 15:40:03 +02:00
|
|
|
|
2012-11-05 16:07:59 +04:00
|
|
|
return ref->mBase->mScript;
|
2010-08-05 15:40:03 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:39:35 +01:00
|
|
|
bool Creature::isEssential(const MWWorld::ConstPtr& ptr) const
|
2012-10-27 13:33:54 +02:00
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, ESM::Creature::Essential);
|
2012-10-27 13:33:54 +02:00
|
|
|
}
|
|
|
|
|
2020-08-27 11:48:59 +00:00
|
|
|
float Creature::getMaxSpeed(const MWWorld::Ptr& ptr) const
|
2013-05-27 18:03:06 +02:00
|
|
|
{
|
2020-06-13 01:04:55 +02:00
|
|
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
|
|
|
|
2018-07-19 20:17:32 +04:00
|
|
|
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
|
|
|
|
return 0.f;
|
|
|
|
|
2014-06-19 02:09:46 +02:00
|
|
|
const GMST& gmst = getGmst();
|
2014-01-15 16:30:16 +01:00
|
|
|
|
|
|
|
const MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
|
|
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
|
|
|
|
|
|
|
float moveSpeed;
|
2014-10-27 15:34:53 +01:00
|
|
|
|
|
|
|
if (getEncumbrance(ptr) > getCapacity(ptr))
|
2014-01-15 16:30:16 +01:00
|
|
|
moveSpeed = 0.0f;
|
2014-08-16 22:38:22 +02:00
|
|
|
else if (canFly(ptr)
|
|
|
|
|| (mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled()))
|
2014-01-15 16:30:16 +01:00
|
|
|
{
|
|
|
|
float flySpeed = 0.01f
|
|
|
|
* (stats.getAttribute(ESM::Attribute::Speed).getModified()
|
2014-08-16 22:38:22 +02:00
|
|
|
+ mageffects.get(ESM::MagicEffect::Levitate).getMagnitude());
|
2018-08-29 18:38:12 +03:00
|
|
|
flySpeed = gmst.fMinFlySpeed->mValue.getFloat()
|
|
|
|
+ flySpeed * (gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());
|
2014-10-27 15:34:53 +01:00
|
|
|
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
2018-08-29 18:38:12 +03:00
|
|
|
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;
|
2014-01-15 16:30:16 +01:00
|
|
|
flySpeed = std::max(0.0f, flySpeed);
|
|
|
|
moveSpeed = flySpeed;
|
|
|
|
}
|
|
|
|
else if (world->isSwimming(ptr))
|
2020-06-13 01:04:55 +02:00
|
|
|
moveSpeed = getSwimSpeed(ptr);
|
2014-01-15 16:30:16 +01:00
|
|
|
else
|
2020-06-13 01:04:55 +02:00
|
|
|
moveSpeed = getWalkSpeed(ptr);
|
2014-01-15 16:30:16 +01:00
|
|
|
|
|
|
|
return moveSpeed;
|
2013-05-27 18:03:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MWMechanics::Movement& Creature::getMovementSettings(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
ensureCustomData(ptr);
|
|
|
|
|
2015-11-29 14:13:14 +01:00
|
|
|
return ptr.getRefData().getCustomData()->asCreatureCustomData().mMovement;
|
2013-05-27 18:03:06 +02:00
|
|
|
}
|
|
|
|
|
2015-12-19 16:13:00 +01:00
|
|
|
bool Creature::hasToolTip(const MWWorld::ConstPtr& ptr) const
|
|
|
|
{
|
2017-03-22 19:55:48 +09:00
|
|
|
if (!ptr.getRefData().getCustomData() || MWBase::Environment::get().getWindowManager()->isGuiMode())
|
2015-12-19 16:13:00 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
2017-07-26 14:42:01 +04:00
|
|
|
|
|
|
|
if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return !customData.mCreatureStats.getAiSequence().isInCombat();
|
2015-12-19 16:13:00 +01:00
|
|
|
}
|
|
|
|
|
2015-12-19 16:29:07 +01:00
|
|
|
MWGui::ToolTipInfo Creature::getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const
|
2012-04-16 22:58:16 +02:00
|
|
|
{
|
2015-12-18 15:27:06 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2012-04-16 22:58:16 +02:00
|
|
|
|
|
|
|
MWGui::ToolTipInfo info;
|
2022-08-16 21:15:03 +02:00
|
|
|
std::string_view name = getName(ptr);
|
2022-08-24 22:16:03 +02:00
|
|
|
info.caption = MyGUI::TextIterator::toTagsString(MWGui::toUString(name));
|
2012-04-16 22:58:16 +02:00
|
|
|
|
|
|
|
std::string text;
|
2012-04-24 02:02:03 +02:00
|
|
|
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
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
|
|
|
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
2012-04-16 22:58:16 +02:00
|
|
|
info.text = text;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
2012-05-15 21:17:00 +02:00
|
|
|
|
2013-03-17 22:29:12 +01:00
|
|
|
float Creature::getArmorRating(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
2019-05-11 19:20:23 +03:00
|
|
|
// Equipment armor rating is deliberately ignored.
|
2014-08-16 22:38:22 +02:00
|
|
|
return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).getMagnitude();
|
2013-03-17 22:29:12 +01:00
|
|
|
}
|
|
|
|
|
2012-05-15 22:31:52 +02:00
|
|
|
float Creature::getCapacity(const MWWorld::Ptr& ptr) const
|
2012-05-15 21:17:00 +02:00
|
|
|
{
|
|
|
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
2018-12-23 15:18:33 +04:00
|
|
|
return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5;
|
2012-05-15 21:17:00 +02:00
|
|
|
}
|
2012-05-15 21:34:00 +02:00
|
|
|
|
2015-12-18 16:44:35 +01:00
|
|
|
int Creature::getServices(const MWWorld::ConstPtr& actor) const
|
2013-05-11 18:38:27 +02:00
|
|
|
{
|
2019-02-08 16:32:34 +03:00
|
|
|
return actor.get<ESM::Creature>()->mBase->mAiData.mServices;
|
2013-05-11 18:38:27 +02:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:41:37 +01:00
|
|
|
bool Creature::isPersistent(const MWWorld::ConstPtr& actor) const
|
2013-05-16 18:50:26 +02:00
|
|
|
{
|
2015-12-18 16:41:37 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = actor.get<ESM::Creature>();
|
2021-07-06 12:37:02 +10:00
|
|
|
return (ref->mBase->mRecordFlags & ESM::FLAG_Persistent) != 0;
|
2013-05-16 18:50:26 +02:00
|
|
|
}
|
|
|
|
|
2023-02-20 23:18:05 +01:00
|
|
|
ESM::RefId Creature::getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const
|
2013-07-17 23:58:21 -07:00
|
|
|
{
|
|
|
|
int type = getSndGenTypeFromName(ptr, name);
|
2019-01-24 23:18:57 +03:00
|
|
|
if (type < 0)
|
2023-02-20 23:18:05 +01:00
|
|
|
return ESM::RefId();
|
2019-01-24 23:18:57 +03:00
|
|
|
|
|
|
|
std::vector<const ESM::SoundGenerator*> sounds;
|
|
|
|
std::vector<const ESM::SoundGenerator*> fallbacksounds;
|
2013-07-17 23:58:21 -07:00
|
|
|
|
2019-01-24 23:18:57 +03:00
|
|
|
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2014-12-12 01:24:35 +01:00
|
|
|
|
2022-10-18 09:26:55 +02:00
|
|
|
const ESM::RefId& ourId = (ref->mBase->mOriginal.empty()) ? ptr.getCellRef().getRefId() : ref->mBase->mOriginal;
|
2019-01-24 23:18:57 +03:00
|
|
|
|
2023-04-20 21:07:53 +02:00
|
|
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
2019-01-24 23:18:57 +03:00
|
|
|
auto sound = store.get<ESM::SoundGenerator>().begin();
|
|
|
|
while (sound != store.get<ESM::SoundGenerator>().end())
|
|
|
|
{
|
2022-10-18 09:26:55 +02:00
|
|
|
if (type == sound->mType && !sound->mCreature.empty() && ourId == sound->mCreature)
|
2019-01-24 23:18:57 +03:00
|
|
|
sounds.push_back(&*sound);
|
|
|
|
if (type == sound->mType && sound->mCreature.empty())
|
|
|
|
fallbacksounds.push_back(&*sound);
|
|
|
|
++sound;
|
|
|
|
}
|
2014-12-14 17:44:03 +01:00
|
|
|
|
2019-01-24 23:18:57 +03:00
|
|
|
if (sounds.empty())
|
|
|
|
{
|
|
|
|
const std::string model = getModel(ptr);
|
|
|
|
if (!model.empty())
|
2013-07-17 23:58:21 -07:00
|
|
|
{
|
2022-06-29 00:32:11 +02:00
|
|
|
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
2019-01-24 23:18:57 +03:00
|
|
|
for (const ESM::Creature& creature : store.get<ESM::Creature>())
|
|
|
|
{
|
|
|
|
if (creature.mId != ourId && creature.mOriginal != ourId && !creature.mModel.empty()
|
2022-05-13 19:28:53 +02:00
|
|
|
&& Misc::StringUtils::ciEqual(
|
2022-06-29 00:32:11 +02:00
|
|
|
model, Misc::ResourceHelpers::correctMeshPath(creature.mModel, vfs)))
|
2019-01-24 23:18:57 +03:00
|
|
|
{
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
const ESM::RefId& fallbackId = !creature.mOriginal.empty() ? creature.mOriginal : creature.mId;
|
2019-01-24 23:18:57 +03:00
|
|
|
sound = store.get<ESM::SoundGenerator>().begin();
|
|
|
|
while (sound != store.get<ESM::SoundGenerator>().end())
|
|
|
|
{
|
2022-10-18 09:26:55 +02:00
|
|
|
if (type == sound->mType && !sound->mCreature.empty() && fallbackId == sound->mCreature)
|
2019-01-24 23:18:57 +03:00
|
|
|
sounds.push_back(&*sound);
|
|
|
|
++sound;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-07-17 23:58:21 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 21:56:02 +02:00
|
|
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
2019-01-24 23:18:57 +03:00
|
|
|
if (!sounds.empty())
|
2022-03-06 21:56:02 +02:00
|
|
|
return sounds[Misc::Rng::rollDice(sounds.size(), prng)]->mSound;
|
2019-01-24 23:18:57 +03:00
|
|
|
if (!fallbacksounds.empty())
|
2022-03-06 21:56:02 +02:00
|
|
|
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size(), prng)]->mSound;
|
2019-01-24 23:18:57 +03:00
|
|
|
|
2023-02-20 23:18:05 +01:00
|
|
|
return ESM::RefId();
|
2013-07-17 23:58:21 -07:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:24:24 +01:00
|
|
|
MWWorld::Ptr Creature::copyToCellImpl(const MWWorld::ConstPtr& ptr, MWWorld::CellStore& cell) const
|
2012-07-25 17:18:17 +04:00
|
|
|
{
|
2015-12-18 16:24:24 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2012-07-25 17:18:17 +04:00
|
|
|
|
2015-11-14 17:12:05 +01:00
|
|
|
return MWWorld::Ptr(cell.insert(ref), &cell);
|
2012-07-25 17:18:17 +04:00
|
|
|
}
|
2013-05-27 18:03:06 +02:00
|
|
|
|
2015-12-18 16:15:40 +01:00
|
|
|
bool Creature::isBipedal(const MWWorld::ConstPtr& ptr) const
|
2014-03-16 08:09:14 +11:00
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, ESM::Creature::Bipedal);
|
2014-03-16 08:09:14 +11:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:16:45 +01:00
|
|
|
bool Creature::canFly(const MWWorld::ConstPtr& ptr) const
|
2014-01-05 15:38:12 +01:00
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, ESM::Creature::Flies);
|
2014-01-05 15:38:12 +01:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:16:45 +01:00
|
|
|
bool Creature::canSwim(const MWWorld::ConstPtr& ptr) const
|
2014-03-16 08:09:14 +11:00
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, static_cast<ESM::Creature::Flags>(ESM::Creature::Swims | ESM::Creature::Bipedal));
|
2014-03-16 08:09:14 +11:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:16:45 +01:00
|
|
|
bool Creature::canWalk(const MWWorld::ConstPtr& ptr) const
|
2014-03-16 08:09:14 +11:00
|
|
|
{
|
2015-08-27 18:36:46 +12:00
|
|
|
return isFlagBitSet(ptr, static_cast<ESM::Creature::Flags>(ESM::Creature::Walks | ESM::Creature::Bipedal));
|
2014-03-16 08:09:14 +11:00
|
|
|
}
|
|
|
|
|
2022-08-14 14:39:58 +02:00
|
|
|
int Creature::getSndGenTypeFromName(const MWWorld::Ptr& ptr, std::string_view name)
|
2013-07-17 23:58:21 -07:00
|
|
|
{
|
|
|
|
if (name == "left")
|
|
|
|
{
|
|
|
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
2019-04-04 17:19:25 +03:00
|
|
|
if (world->isFlying(ptr))
|
|
|
|
return -1;
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
2014-10-05 22:26:08 +02:00
|
|
|
if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::SwimLeft;
|
2013-07-17 23:58:21 -07:00
|
|
|
if (world->isOnGround(ptr))
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::LeftFoot;
|
2013-07-17 23:58:21 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (name == "right")
|
|
|
|
{
|
|
|
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
2019-04-04 17:19:25 +03:00
|
|
|
if (world->isFlying(ptr))
|
|
|
|
return -1;
|
2015-06-01 21:41:13 +02:00
|
|
|
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
2014-10-05 22:26:08 +02:00
|
|
|
if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::SwimRight;
|
2013-07-17 23:58:21 -07:00
|
|
|
if (world->isOnGround(ptr))
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::RightFoot;
|
2013-07-17 23:58:21 -07:00
|
|
|
return -1;
|
|
|
|
}
|
2013-08-08 01:35:22 -07:00
|
|
|
if (name == "swimleft")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::SwimLeft;
|
2013-08-08 01:35:22 -07:00
|
|
|
if (name == "swimright")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::SwimRight;
|
2013-07-17 23:58:21 -07:00
|
|
|
if (name == "moan")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::Moan;
|
2013-07-17 23:58:21 -07:00
|
|
|
if (name == "roar")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::Roar;
|
2013-07-17 23:58:21 -07:00
|
|
|
if (name == "scream")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::Scream;
|
2013-07-24 10:36:04 -07:00
|
|
|
if (name == "land")
|
2018-10-14 20:37:40 +03:00
|
|
|
return ESM::SoundGenerator::Land;
|
2013-07-17 23:58:21 -07:00
|
|
|
|
2022-08-14 14:39:58 +02:00
|
|
|
throw std::runtime_error("Unexpected soundgen type: " + std::string(name));
|
2013-07-17 23:58:21 -07:00
|
|
|
}
|
|
|
|
|
2018-12-23 15:18:33 +04:00
|
|
|
float Creature::getSkill(const MWWorld::Ptr& ptr, int skill) const
|
2014-01-15 15:50:45 +01:00
|
|
|
{
|
|
|
|
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
|
|
|
|
2023-04-20 21:07:53 +02:00
|
|
|
const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skill);
|
2014-01-15 15:50:45 +01:00
|
|
|
|
|
|
|
switch (skillRecord->mData.mSpecialization)
|
|
|
|
{
|
|
|
|
case ESM::Class::Combat:
|
|
|
|
return ref->mBase->mData.mCombat;
|
|
|
|
case ESM::Class::Magic:
|
|
|
|
return ref->mBase->mData.mMagic;
|
|
|
|
case ESM::Class::Stealth:
|
|
|
|
return ref->mBase->mData.mStealth;
|
|
|
|
default:
|
|
|
|
throw std::runtime_error("invalid specialisation");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-18 16:38:14 +01:00
|
|
|
int Creature::getBloodTexture(const MWWorld::ConstPtr& ptr) const
|
2014-01-17 10:52:44 +01:00
|
|
|
{
|
2018-12-31 17:55:46 +03:00
|
|
|
return ptr.get<ESM::Creature>()->mBase->mBloodType;
|
2014-01-17 10:52:44 +01:00
|
|
|
}
|
|
|
|
|
2014-02-01 17:36:23 +01:00
|
|
|
void Creature::readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
|
|
|
{
|
2015-01-19 23:29:06 +01:00
|
|
|
if (!state.mHasCustomState)
|
|
|
|
return;
|
|
|
|
|
2021-08-17 12:29:28 +10:00
|
|
|
const ESM::CreatureState& creatureState = state.asCreatureState();
|
|
|
|
|
2015-06-30 03:58:23 +02:00
|
|
|
if (state.mVersion > 0)
|
2014-06-18 22:59:18 +02:00
|
|
|
{
|
2015-06-30 03:58:23 +02:00
|
|
|
if (!ptr.getRefData().getCustomData())
|
|
|
|
{
|
2021-12-24 23:17:50 +01:00
|
|
|
if (creatureState.mCreatureStats.mMissingACDT)
|
2021-08-17 12:29:28 +10:00
|
|
|
ensureCustomData(ptr);
|
2015-06-30 03:58:23 +02:00
|
|
|
else
|
2021-08-17 12:29:28 +10:00
|
|
|
{
|
|
|
|
// Create a CustomData, but don't fill it from ESM records (not needed)
|
2022-05-29 13:24:48 +02:00
|
|
|
auto data = std::make_unique<CreatureCustomData>();
|
2014-06-18 22:59:18 +02:00
|
|
|
|
2021-08-17 12:29:28 +10:00
|
|
|
if (hasInventoryStore(ptr))
|
|
|
|
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
|
|
|
|
else
|
|
|
|
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
|
|
|
|
|
|
|
|
ptr.getRefData().setCustomData(std::move(data));
|
|
|
|
}
|
2015-06-30 03:58:23 +02:00
|
|
|
}
|
2014-06-18 22:59:18 +02:00
|
|
|
}
|
2015-06-30 03:58:23 +02:00
|
|
|
else
|
|
|
|
ensureCustomData(
|
|
|
|
ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.
|
2014-02-01 17:36:23 +01:00
|
|
|
|
2015-11-29 14:13:14 +01:00
|
|
|
CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
2021-08-17 12:29:28 +10:00
|
|
|
|
2020-03-17 14:15:19 +04:00
|
|
|
customData.mContainerStore->readState(creatureState.mInventory);
|
2020-07-28 08:33:28 +02:00
|
|
|
bool spellsInitialised = customData.mCreatureStats.getSpells().setSpells(ptr.get<ESM::Creature>()->mBase->mId);
|
|
|
|
if (spellsInitialised)
|
|
|
|
customData.mCreatureStats.getSpells().clear();
|
2020-03-17 14:15:19 +04:00
|
|
|
customData.mCreatureStats.readState(creatureState.mCreatureStats);
|
2014-02-01 17:36:23 +01:00
|
|
|
}
|
|
|
|
|
2015-12-18 00:18:06 +01:00
|
|
|
void Creature::writeAdditionalState(const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
2014-02-01 17:36:23 +01:00
|
|
|
{
|
2015-01-19 23:29:06 +01:00
|
|
|
if (!ptr.getRefData().getCustomData())
|
|
|
|
{
|
|
|
|
state.mHasCustomState = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-02 19:33:27 +02:00
|
|
|
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
|
|
|
if (ptr.getRefData().getCount() <= 0
|
|
|
|
&& (!isFlagBitSet(ptr, ESM::Creature::Respawn) || !customData.mCreatureStats.isDead()))
|
2020-03-04 15:14:22 +04:00
|
|
|
{
|
|
|
|
state.mHasCustomState = false;
|
2015-01-19 23:29:06 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-17 14:15:19 +04:00
|
|
|
ESM::CreatureState& creatureState = state.asCreatureState();
|
|
|
|
customData.mContainerStore->writeState(creatureState.mInventory);
|
|
|
|
customData.mCreatureStats.writeState(creatureState.mCreatureStats);
|
2014-02-01 17:36:23 +01:00
|
|
|
}
|
|
|
|
|
2015-12-18 16:28:20 +01:00
|
|
|
int Creature::getBaseGold(const MWWorld::ConstPtr& ptr) const
|
2014-03-28 14:21:38 -04:00
|
|
|
{
|
|
|
|
return ptr.get<ESM::Creature>()->mBase->mData.mGold;
|
|
|
|
}
|
|
|
|
|
2014-05-17 09:05:41 +02:00
|
|
|
void Creature::respawn(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
2018-10-28 17:03:38 +03:00
|
|
|
const MWMechanics::CreatureStats& creatureStats = getCreatureStats(ptr);
|
2016-02-27 12:53:07 +01:00
|
|
|
if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())
|
|
|
|
return;
|
2018-06-11 22:29:32 +04:00
|
|
|
|
|
|
|
if (!creatureStats.isDeathAnimationFinished())
|
|
|
|
return;
|
2016-02-27 12:53:07 +01:00
|
|
|
|
|
|
|
const MWWorld::Store<ESM::GameSetting>& gmst
|
2023-04-20 21:07:53 +02:00
|
|
|
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
2018-08-29 18:38:12 +03:00
|
|
|
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat();
|
|
|
|
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat();
|
2016-02-27 12:53:07 +01:00
|
|
|
|
|
|
|
float delay
|
|
|
|
= ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);
|
|
|
|
|
|
|
|
if (isFlagBitSet(ptr, ESM::Creature::Respawn)
|
|
|
|
&& creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
2014-05-17 09:05:41 +02:00
|
|
|
{
|
2015-01-11 12:20:22 +13:00
|
|
|
if (ptr.getCellRef().hasContentFile())
|
2014-05-17 09:05:41 +02:00
|
|
|
{
|
|
|
|
if (ptr.getRefData().getCount() == 0)
|
2019-08-06 21:30:21 +02:00
|
|
|
{
|
2014-05-17 09:05:41 +02:00
|
|
|
ptr.getRefData().setCount(1);
|
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& script = getScript(ptr);
|
2019-08-06 21:30:21 +02:00
|
|
|
if (!script.empty())
|
|
|
|
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, ptr);
|
|
|
|
}
|
2014-05-17 09:05:41 +02:00
|
|
|
|
2015-12-14 02:57:55 +01:00
|
|
|
MWBase::Environment::get().getWorld()->removeContainerScripts(ptr);
|
2021-07-15 22:31:26 +02:00
|
|
|
MWBase::Environment::get().getWindowManager()->onDeleteCustomData(ptr);
|
2018-10-09 10:21:12 +04:00
|
|
|
ptr.getRefData().setCustomData(nullptr);
|
2016-02-07 19:43:38 +01:00
|
|
|
|
|
|
|
// Reset to original position
|
2021-04-11 18:18:10 +02:00
|
|
|
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
|
2021-09-14 16:53:57 +02:00
|
|
|
MWBase::Environment::get().getWorld()->rotateObject(
|
|
|
|
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
2014-05-17 09:05:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-18 16:12:35 +01:00
|
|
|
int Creature::getBaseFightRating(const MWWorld::ConstPtr& ptr) const
|
2014-12-14 19:35:34 +01:00
|
|
|
{
|
2015-12-18 16:12:35 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2014-12-14 19:35:34 +01:00
|
|
|
return ref->mBase->mAiData.mFight;
|
|
|
|
}
|
2014-12-15 13:34:04 +01:00
|
|
|
|
2015-12-18 16:46:02 +01:00
|
|
|
void Creature::adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool /* rendering */) const
|
2014-12-15 13:34:04 +01:00
|
|
|
{
|
2015-12-18 16:46:02 +01:00
|
|
|
const MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
|
2014-12-15 13:34:04 +01:00
|
|
|
scale *= ref->mBase->mScale;
|
|
|
|
}
|
2020-06-02 21:59:37 +02:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void Creature::setBaseAISetting(const ESM::RefId& id, MWMechanics::AiSetting setting, int value) const
|
2020-06-02 21:59:37 +02:00
|
|
|
{
|
|
|
|
MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);
|
|
|
|
}
|
2020-06-13 01:04:55 +02:00
|
|
|
|
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type
The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID
Slowly going through all the changes to make, still hundreds of errors
a lot of functions/structures use std::string or stringview to designate an ID. So it takes time
Continues slowly replacing ids. There are technically more and more compilation errors
I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type
Continue moving forward, changes to the stores
slowly moving along
Starting to see the fruit of those changes.
still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type.
More replacements. Things are starting to get easier
I can see more and more often the issue is that the function is awaiting a RefId, but is given a string
there is less need to go down functions and to fix a long list of them.
Still moving forward, and for the first time error count is going down!
Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably
Cells are back to using string for the name, haven't fixed everything yet. Many other changes
Under the bar of 400 compilation errors.
more good progress <100 compile errors!
More progress
Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string
some more progress on other fronts
Mostly game settings clean
one error opened a lot of other errors. Down to 18, but more will prbably appear
only link errors left??
Fixed link errors
OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
|
|
|
void Creature::modifyBaseInventory(const ESM::RefId& actorId, const ESM::RefId& itemId, int amount) const
|
2020-07-26 11:07:18 +02:00
|
|
|
{
|
|
|
|
MWMechanics::modifyBaseInventory<ESM::Creature>(actorId, itemId, amount);
|
|
|
|
}
|
|
|
|
|
2020-06-13 01:04:55 +02:00
|
|
|
float Creature::getWalkSpeed(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
|
|
|
const GMST& gmst = getGmst();
|
|
|
|
|
|
|
|
return gmst.fMinWalkSpeedCreature->mValue.getFloat()
|
|
|
|
+ 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
|
|
|
|
* (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
|
|
|
|
}
|
|
|
|
|
|
|
|
float Creature::getRunSpeed(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
return getWalkSpeed(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
|
|
|
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
|
|
|
|
2020-06-14 18:01:22 +02:00
|
|
|
return getSwimSpeedImpl(ptr, getGmst(), mageffects, getWalkSpeed(ptr));
|
2020-06-13 01:04:55 +02:00
|
|
|
}
|
2010-08-03 13:03:08 +02:00
|
|
|
}
|