Merge branch 'master' into automove

pull/578/head
Bret Curtis 5 years ago committed by GitHub
commit cd97d0c61c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,7 @@
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #4021: Attributes and skills are not stored as floats
Bug #4623: Corprus implementation is incorrect
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format
@ -25,6 +26,7 @@
Bug #5427: GetDistance unknown ID error is misleading
Bug #5435: Enemies can't hurt the player when collision is off
Bug #5441: Enemies can't push a player character when in critical strike stance
Bug #5451: Magic projectiles don't disappear with the caster
Bug #5452: Autowalk is being included in savegames
Feature #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines
@ -299,7 +301,7 @@
Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window
Feature #5193: Weapon sheathing
Feature #5193: Shields sheathing
Feature #5201: Editor: Show tool outline in scene view, when using editmodes
Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape

@ -13,7 +13,16 @@ MISSINGTOOLS=0
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; }
MISSINGPYTHON=0
if ! command -v python >/dev/null 2>&1; then
echo "Warning: Python is not on the path, automatic Qt installation impossible."
MISSINGPYTHON=1
elif ! python --version >/dev/null 2>&1; then
echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible."
echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings."
MISSINGPYTHON=1
fi
if [ $MISSINGTOOLS -ne 0 ]; then
wrappedExit 1
@ -745,6 +754,11 @@ fi
if [ -d 'Qt/5.15.0' ]; then
printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then
if [ $MISSINGPYTHON -ne 0 ]; then
echo "Can't be automatically installed without Python."
wrappedExit 1
fi
pushd "$DEPS" > /dev/null
if ! [ -d 'aqt-venv' ]; then
echo " Creating Virtualenv for aqt..."

@ -15,17 +15,13 @@ set(GAME_HEADER
engine.hpp
)
if (BULLET_USE_DOUBLES)
add_definitions(-DBT_USE_DOUBLE_PRECISION)
endif()
source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager
)
add_openmw_dir (mwinput
@ -71,7 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader
cellpreloader datetimemanager
)
add_openmw_dir (mwphysics

@ -48,6 +48,7 @@ namespace ESM
struct EffectList;
struct CreatureLevList;
struct ItemLevList;
struct TimeStamp;
}
namespace MWRender
@ -204,24 +205,14 @@ namespace MWBase
virtual void advanceTime (double hours, bool incremental = false) = 0;
///< Advance in-game time.
virtual void setHour (double hour) = 0;
///< Set in-game time hour.
virtual void setMonth (int month) = 0;
///< Set in-game time month.
virtual void setDay (int day) = 0;
///< Set in-game time day.
virtual int getDay() const = 0;
virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp.
///< Return current in-game time and number of day since new game start.
virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0;
///< Return current in-game date and time.
virtual bool toggleSky() = 0;
///< \return Resulting mode

@ -276,7 +276,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
int armorSkillType = getEquipmentSkill(ptr);
int armorSkill = actor.getClass().getSkill(actor, armorSkillType);
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
const MWBase::World *world = MWBase::Environment::get().getWorld();
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger();

@ -605,7 +605,7 @@ namespace MWClass
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
return static_cast<float>(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5);
return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5;
}
int Creature::getServices(const MWWorld::ConstPtr &actor) const
@ -745,7 +745,7 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();

@ -108,7 +108,7 @@ namespace MWClass
virtual bool canSwim (const MWWorld::ConstPtr &ptr) const;
virtual bool canWalk (const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;

@ -125,7 +125,7 @@ namespace MWClass
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();

@ -127,8 +127,8 @@ namespace
}
// initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
int multiplier = 3;
@ -1011,7 +1011,7 @@ namespace MWClass
gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
(1.0f - Npc::getNormalizedEncumbrance(ptr));
float a = static_cast<float>(getSkill(ptr, ESM::Skill::Acrobatics));
float a = getSkill(ptr, ESM::Skill::Acrobatics);
float b = 0.0f;
if(a > 50.0f)
{
@ -1136,7 +1136,7 @@ namespace MWClass
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
@ -1283,7 +1283,7 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell);
}
int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
{
return getNpcStats(ptr).getSkill(skill).getModified();
}

@ -129,7 +129,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;

@ -95,9 +95,9 @@ namespace MWGui
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)
value.setBase(std::min(100, value.getBase()+1));
value.setBase(std::min(100.f, value.getBase()+1));
else
value.setBase(std::max(0, value.getBase()-1));
value.setBase(std::max(0.f, value.getBase()-1));
}
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

@ -157,7 +157,7 @@ namespace MWGui
mAttributeValues[i]->setEnabled(true);
availableAttributes++;
int mult = pcStats.getLevelupAttributeMultiplier (i);
float mult = pcStats.getLevelupAttributeMultiplier (i);
mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());
text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult));
}

@ -22,7 +22,7 @@ namespace MWGui
{
MWWorld::Ptr player = MWMechanics::getPlayer();
mSourceModel = sourceModel;
int chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
mSourceModel->update();

@ -159,7 +159,7 @@ namespace MWGui
for (int i=0; ids[i]; ++i)
if (ids[i]==id)
{
setText (id, std::to_string(value.getModified()));
setText (id, std::to_string(static_cast<int>(value.getModified())));
MyGUI::TextBox* box;
getWidget(box, id);

@ -74,11 +74,11 @@ namespace MWGui
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
// NPC can train you in his best 3 skills
std::vector< std::pair<int, int> > skills;
std::vector< std::pair<int, float> > skills;
for (int i=0; i<ESM::Skill::Length; ++i)
{
int value = actor.getClass().getSkill(actor, i);
float value = actor.getClass().getSkill(actor, i);
skills.push_back(std::make_pair(i, value));
}

@ -150,11 +150,10 @@ namespace MWGui
if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12;
std::string dateTimeText =
MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " "
+ month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay())
+ ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp();
int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay();
std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}";
std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour);
mDateTimeText->setCaptionWithReplacing (dateTimeText);
}

@ -132,7 +132,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
health = 0.1f * endurance;
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
@ -765,7 +765,7 @@ namespace MWMechanics
{
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
float base = 1.f;
if (ptr == getPlayer())
@ -844,7 +844,7 @@ namespace MWMechanics
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat ();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1)
@ -871,7 +871,7 @@ namespace MWMechanics
return;
// Restore fatigue
int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
@ -2051,6 +2051,8 @@ namespace MWMechanics
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
continue;

@ -44,11 +44,6 @@ namespace MWMechanics
return false;
}
int AiActivate::getTypeId() const
{
return TypeIdActivate;
}
void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());

@ -29,12 +29,13 @@ namespace MWMechanics
AiActivate(const ESM::AiSequence::AiActivate* activate);
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdActivate; }
void writeState(ESM::AiSequence::AiSequence& sequence) const final;
private:
std::string mObjectId;
const std::string mObjectId;
};
}
#endif // GAME_MWMECHANICS_AIACTIVATE_H

@ -72,16 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return false;
}
int MWMechanics::AiAvoidDoor::getTypeId() const
{
return TypeIdAvoidDoor;
}
unsigned int MWMechanics::AiAvoidDoor::getPriority() const
{
return 2;
}
bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{
return (actorPos - mLastPos).length2() < 10 * 10;

@ -24,16 +24,20 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; }
unsigned int getPriority() const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private:
float mDuration;
MWWorld::ConstPtr mDoorPtr;
const MWWorld::ConstPtr mDoorPtr;
osg::Vec3f mLastPos;
int mDirection;

@ -31,13 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
return true;
}
int MWMechanics::AiBreathe::getTypeId() const
{
return TypeIdBreathe;
}
unsigned int MWMechanics::AiBreathe::getPriority() const
{
return 2;
}

@ -12,13 +12,16 @@ namespace MWMechanics
public:
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdBreathe; }
unsigned int getPriority() const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
};
}
#endif

@ -10,12 +10,22 @@
#include "creaturestats.hpp"
#include "steering.hpp"
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
namespace MWMechanics
{
namespace
{
float getInitialDistance(const std::string& spellId)
{
ActionSpell action = ActionSpell(spellId);
bool isRanged;
mDistance = action.getCombatRange(isRanged);
return action.getCombatRange(isRanged);
}
}
}
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId))
{
}
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
@ -79,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const
return target;
}
int MWMechanics::AiCast::getTypeId() const
{
return AiPackage::TypeIdCast;
}
unsigned int MWMechanics::AiCast::getPriority() const
{
return 3;
}

@ -17,21 +17,25 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdCast; }
MWWorld::Ptr getTarget() const final;
unsigned int getPriority() const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 3;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private:
std::string mTargetId;
std::string mSpellId;
const std::string mTargetId;
const std::string mSpellId;
bool mCasting;
bool mManual;
float mDistance;
const bool mManual;
const float mDistance;
};
}

@ -406,16 +406,6 @@ namespace MWMechanics
}
}
int AiCombat::getTypeId() const
{
return TypeIdCombat;
}
unsigned int AiCombat::getPriority() const
{
return 1;
}
MWWorld::Ptr AiCombat::getTarget() const
{
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);

@ -104,18 +104,22 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdCombat; }
unsigned int getPriority() const final;
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 1;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
///Returns target ID
MWWorld::Ptr getTarget() const final;
void writeState(ESM::AiSequence::AiSequence &sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
private:
/// Returns true if combat should end
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);

@ -26,7 +26,6 @@ namespace MWMechanics
, mCellY(std::numeric_limits<int>::max())
{
mTargetActorRefId = actorId;
mMaxDist = 450;
}
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z)
@ -35,24 +34,20 @@ namespace MWMechanics
, mCellY(std::numeric_limits<int>::max())
{
mTargetActorRefId = actorId;
mMaxDist = 450;
}
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
: mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
, mMaxDist(450)
// mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration.
// The exact value of mDuration only matters for repeating packages.
// Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
, mDuration(escort->mRemainingDuration > 0)
, mRemainingDuration(escort->mRemainingDuration)
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
mTargetActorRefId = escort->mTargetId;
mTargetActorId = escort->mTargetActorId;
// mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration.
// The exact value of mDuration only matters for repeating packages.
if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
mDuration = 1;
else
mDuration = 0;
}
bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -100,11 +95,6 @@ namespace MWMechanics
return false;
}
int AiEscort::getTypeId() const
{
return TypeIdEscort;
}
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());

@ -32,11 +32,15 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdEscort; }
bool useVariableSpeed() const final { return true; }
bool sideWithTarget() const final { return true; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mSideWithTarget = true;
return options;
}
void writeState(ESM::AiSequence::AiSequence &sequence) const final;
@ -45,16 +49,16 @@ namespace MWMechanics
osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private:
std::string mCellId;
float mX;
float mY;
float mZ;
float mMaxDist;
float mDuration; // In hours
const std::string mCellId;
const float mX;
const float mY;
const float mZ;
float mMaxDist = 450;
const float mDuration; // In hours
float mRemainingDuration; // In hours
int mCellX;
int mCellY;
const int mCellX;
const int mCellY;
};
}
#endif

@ -14,13 +14,3 @@ bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3();
return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f));
}
int MWMechanics::AiFace::getTypeId() const
{
return AiPackage::TypeIdFace;
}
unsigned int MWMechanics::AiFace::getPriority() const
{
return 2;
}

@ -12,15 +12,20 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdFace; }
unsigned int getPriority() const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private:
float mTargetX, mTargetY;
const float mTargetX;
const float mTargetY;
};
}

@ -16,25 +16,24 @@
namespace MWMechanics
{
int AiFollow::mFollowIndexCounter = 0;
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = actorId;
}
AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = actorId;
}
AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = actor.getCellRef().getRefId();
@ -42,7 +41,7 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y,
}
AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = actor.getCellRef().getRefId();
@ -50,7 +49,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float d
}
AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
: mAlwaysFollow(true), mCommanded(commanded), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!commanded))
, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = actor.getCellRef().getRefId();
@ -58,18 +58,18 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
}
AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration)
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded))
, mAlwaysFollow(follow->mAlwaysFollow)
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration.
// The exact value of mDuration only matters for repeating packages.
// Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
, mDuration(follow->mRemainingDuration)
, mRemainingDuration(follow->mRemainingDuration)
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)
{
mTargetActorRefId = follow->mTargetId;
mTargetActorId = follow->mTargetActorId;
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration.
// The exact value of mDuration only matters for repeating packages.
if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
mDuration = 1;
else
mDuration = 0;
}
bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -201,14 +201,9 @@ std::string AiFollow::getFollowedActor()
return mTargetActorRefId;
}
int AiFollow::getTypeId() const
{
return TypeIdFollow;
}
bool AiFollow::isCommanded() const
{
return mCommanded;
return !mOptions.mShouldCancelPreviousAi;
}
void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
@ -222,7 +217,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow;
follow->mCommanded = mCommanded;
follow->mCommanded = isCommanded();
follow->mActive = mActive;
ESM::AiSequence::AiPackageContainer package;

@ -53,15 +53,18 @@ namespace MWMechanics
AiFollow(const ESM::AiSequence::AiFollow* follow);
bool sideWithTarget() const final { return true; }
bool followTargetThroughDoors() const final { return true; }
bool shouldCancelPreviousAi() const final { return !mCommanded; }
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdFollow; }
bool useVariableSpeed() const final { return true; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mSideWithTarget = true;
options.mFollowTargetThroughDoors = true;
return options;
}
/// Returns the actor being followed
std::string getFollowedActor();
@ -86,16 +89,15 @@ namespace MWMechanics
private:
/// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow;
bool mCommanded;
float mDuration; // Hours
const bool mAlwaysFollow;
const float mDuration; // Hours
float mRemainingDuration; // Hours
float mX;
float mY;
float mZ;
std::string mCellId;
const float mX;
const float mY;
const float mZ;
const std::string mCellId;
bool mActive; // have we spotted the target?
int mFollowIndex;
const int mFollowIndex;
static int mFollowIndexCounter;
};

@ -24,7 +24,9 @@
#include <osg/Quat>
MWMechanics::AiPackage::AiPackage() :
MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) :
mTypeId(typeId),
mOptions(options),
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
mTargetActorRefId(""),
mTargetActorId(-1),
@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
return MWWorld::Ptr();
}
bool MWMechanics::AiPackage::sideWithTarget() const
{
return false;
}
bool MWMechanics::AiPackage::followTargetThroughDoors() const
{
return false;
}
bool MWMechanics::AiPackage::canCancel() const
{
return true;
}
bool MWMechanics::AiPackage::shouldCancelPreviousAi() const
{
return true;
}
bool MWMechanics::AiPackage::getRepeat() const
{
return false;
}
void MWMechanics::AiPackage::reset()
{
// reset all members

@ -55,11 +55,39 @@ namespace MWMechanics
TypeIdCast = 11
};
///Default constructor
AiPackage();
struct Options
{
unsigned int mPriority = 0;
bool mUseVariableSpeed = false;
bool mSideWithTarget = false;
bool mFollowTargetThroughDoors = false;
bool mCanCancel = true;
bool mShouldCancelPreviousAi = true;
bool mRepeat = false;
bool mAlwaysActive = false;
constexpr Options withRepeat(bool value)
{
mRepeat = value;
return *this;
}
constexpr Options withShouldCancelPreviousAi(bool value)
{
mShouldCancelPreviousAi = value;
return *this;
}
};
AiPackage(TypeId typeId, const Options& options);
virtual ~AiPackage() = default;
static constexpr Options makeDefaultOptions()
{
return Options{};
}
///Clones the package
virtual std::unique_ptr<AiPackage> clone() const = 0;
@ -69,13 +97,13 @@ namespace MWMechanics
/// Returns the TypeID of the AiPackage
/// \see enum TypeId
virtual int getTypeId() const = 0;
TypeId getTypeId() const { return mTypeId; }
/// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;}
unsigned int getPriority() const { return mOptions.mPriority; }
/// Check if package use movement with variable speed
virtual bool useVariableSpeed() const { return false;}
bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; }
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
@ -89,24 +117,24 @@ namespace MWMechanics
virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); };
/// Return true if having this AiPackage makes the actor side with the target in fights (default false)
virtual bool sideWithTarget() const;
bool sideWithTarget() const { return mOptions.mSideWithTarget; }
/// Return true if the actor should follow the target through teleport doors (default false)
virtual bool followTargetThroughDoors() const;
bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; }
/// Can this Ai package be canceled? (default true)
virtual bool canCancel() const;
bool canCancel() const { return mOptions.mCanCancel; }
/// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)?
virtual bool shouldCancelPreviousAi() const;
bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; }
/// Return true if this package should repeat. Currently only used for Wander packages.
virtual bool getRepeat() const;
bool getRepeat() const { return mOptions.mRepeat; }
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
// Return true if any loaded actor with this AI package must be active.
virtual bool alwaysActive() const { return false; }
/// Return true if any loaded actor with this AI package must be active.
bool alwaysActive() const { return mOptions.mAlwaysActive; }
/// Reset pathfinding state
void reset();
@ -139,6 +167,9 @@ namespace MWMechanics
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;
const TypeId mTypeId;
const Options mOptions;
// TODO: all this does not belong here, move into temporary storage
PathFinder mPathFinder;
ObstacleCheck mObstacleCheck;

@ -66,11 +66,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
return false;
}
int AiPursue::getTypeId() const
{
return TypeIdPursue;
}
MWWorld::Ptr AiPursue::getTarget() const
{
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);

@ -27,14 +27,20 @@ namespace MWMechanics
AiPursue(const ESM::AiSequence::AiPursue* pursue);
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdPursue; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
MWWorld::Ptr getTarget() const final;
void writeState (ESM::AiSequence::AiSequence& sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
};
}
#endif

@ -338,7 +338,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
dest = actor.getRefData().getPosition().asVec3();
}
MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true);
MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z());
stack(travelPackage, actor, false);
}
@ -478,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
}
case ESM::AiSequence::Ai_Travel:
{
package.reset(new AiTravel(static_cast<ESM::AiSequence::AiTravel*>(it->mPackage)));
const auto source = static_cast<const ESM::AiSequence::AiTravel*>(it->mPackage);
if (source->mHidden)
package.reset(new AiInternalTravel(source));
else
package.reset(new AiTravel(source));
break;
}
case ESM::AiSequence::Ai_Escort:

@ -27,14 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
namespace MWMechanics
{
AiTravel::AiTravel(float x, float y, float z, bool hidden)
: mX(x),mY(y),mZ(z),mHidden(hidden)
AiTravel::AiTravel(float x, float y, float z, AiTravel*)
: mX(x), mY(y), mZ(z), mHidden(false)
{
}
AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
: TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
{
}
AiTravel::AiTravel(float x, float y, float z)
: AiTravel(x, y, z, this)
{
}
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(travel->mHidden)
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
{
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
assert(!travel->mHidden);
}
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -78,11 +90,6 @@ namespace MWMechanics
return false;
}
int AiTravel::getTypeId() const
{
return mHidden ? TypeIdInternalTravel : TypeIdTravel;
}
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
{
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3()))
@ -107,5 +114,20 @@ namespace MWMechanics
package.mPackage = travel.release();
sequence.mPackages.push_back(package);
}
AiInternalTravel::AiInternalTravel(float x, float y, float z)
: AiTravel(x, y, z, this)
{
}
AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel)
: AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this)
{
}
std::unique_ptr<AiPackage> AiInternalTravel::clone() const
{
return std::make_unique<AiInternalTravel>(*this);
}
}

@ -13,12 +13,18 @@ namespace AiSequence
namespace MWMechanics
{
struct AiInternalTravel;
/// \brief Causes the AI to travel to the specified point
class AiTravel final : public TypedAiPackage<AiTravel>
class AiTravel : public TypedAiPackage<AiTravel>
{
public:
/// Default constructor
AiTravel(float x, float y, float z, bool hidden = false);
AiTravel(float x, float y, float z, AiTravel* derived);
AiTravel(float x, float y, float z, AiInternalTravel* derived);
AiTravel(float x, float y, float z);
AiTravel(const ESM::AiSequence::AiTravel* travel);
/// Simulates the passing of time
@ -28,20 +34,35 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
bool useVariableSpeed() const final { return true; }
static constexpr TypeId getTypeId() { return TypeIdTravel; }
bool alwaysActive() const final { return true; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mAlwaysActive = true;
return options;
}
osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private:
float mX;
float mY;
float mZ;
const float mX;
const float mY;
const float mZ;
const bool mHidden;
};
struct AiInternalTravel final : public AiTravel
{
AiInternalTravel(float x, float y, float z);
explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel);
static constexpr TypeId getTypeId() { return TypeIdInternalTravel; }
bool mHidden;
std::unique_ptr<AiPackage> clone() const final;
};
}

@ -1,5 +1,7 @@
#include "aiwander.hpp"
#include <algorithm>
#include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp>
#include <components/esm/aisequence.hpp>
@ -33,6 +35,8 @@ namespace MWMechanics
// distance must be long enough that NPC will need to move to get there.
static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2;
static const std::size_t MAX_IDLE_SIZE = 8;
const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] =
{
std::string("idle2"),
@ -94,25 +98,29 @@ namespace MWMechanics
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
}
}
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false)
std::vector<unsigned char> getInitialIdle(const std::vector<unsigned char>& idle)
{
mIdle.resize(8, 0);
init();
std::vector<unsigned char> result(MAX_IDLE_SIZE, 0);
std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin());
return result;
}
void AiWander::init()
std::vector<unsigned char> getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE])
{
// NOTE: mDistance and mDuration must be set already
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
}
}
if(mDistance < 0)
mDistance = 0;
if(mDuration < 0)
mDuration = 0;
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),
mDistance(std::max(0, distance)),
mDuration(std::max(0, duration)),
mRemainingDuration(duration), mTimeOfDay(timeOfDay),
mIdle(getInitialIdle(idle)),
mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false)
{
}
/*
@ -235,7 +243,6 @@ namespace MWMechanics
stopWalking(actor);
// Reset package so it can be used again
mRemainingDuration=mDuration;
init();
return true;
}
@ -303,11 +310,6 @@ namespace MWMechanics
return false; // AiWander package not yet completed
}
bool AiWander::getRepeat() const
{
return mRepeat;
}
osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const
{
if (mHasDestination)
@ -593,11 +595,6 @@ namespace MWMechanics
}
}
int AiWander::getTypeId() const
{
return TypeIdWander;
}
void AiWander::stopWalking(const MWWorld::Ptr& actor)
{
mPathFinder.clearPath();
@ -867,7 +864,7 @@ namespace MWMechanics
assert (mIdle.size() == 8);
for (int i=0; i<8; ++i)
wander->mData.mIdle[i] = mIdle[i];
wander->mData.mShouldRepeat = mRepeat;
wander->mData.mShouldRepeat = mOptions.mRepeat;
wander->mStoredInitialActorPosition = mStoredInitialActorPosition;
if (mStoredInitialActorPosition)
wander->mInitialActorPosition = mInitialActorPosition;
@ -879,11 +876,12 @@ namespace MWMechanics
}
AiWander::AiWander (const ESM::AiSequence::AiWander* wander)
: mDistance(wander->mData.mDistance)
, mDuration(wander->mData.mDuration)
: TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0))
, mDistance(std::max(static_cast<short>(0), wander->mData.mDistance))
, mDuration(std::max(static_cast<short>(0), wander->mData.mDuration))
, mRemainingDuration(wander->mDurationData.mRemainingDuration)
, mTimeOfDay(wander->mData.mTimeOfDay)
, mRepeat(wander->mData.mShouldRepeat != 0)
, mIdle(getInitialIdle(wander->mData.mIdle))
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
, mHasDestination(false)
, mDestination(osg::Vec3f(0, 0, 0))
@ -891,11 +889,7 @@ namespace MWMechanics
{
if (mStoredInitialActorPosition)
mInitialActorPosition = wander->mInitialActorPosition;
for (int i=0; i<8; ++i)
mIdle.push_back(wander->mData.mIdle[i]);
if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
mRemainingDuration = mDuration;
init();
}
}

@ -93,16 +93,20 @@ namespace MWMechanics
bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdWander; }
bool useVariableSpeed() const final { return true; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mRepeat = false;
return options;
}
void writeState(ESM::AiSequence::AiSequence &sequence) const final;
void fastForward(const MWWorld::Ptr& actor, AiState& state) final;
bool getRepeat() const final;
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final;
osg::Vec3f getDestination() const final
@ -114,8 +118,6 @@ namespace MWMechanics
}
private:
// NOTE: mDistance and mDuration must be set already
void init();
void stopWalking(const MWWorld::Ptr& actor);
/// Have the given actor play an idle animation
@ -136,12 +138,11 @@ namespace MWMechanics
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
int mDistance; // how far the actor can wander from the spawn point
int mDuration;
const int mDistance; // how far the actor can wander from the spawn point
const int mDuration;
float mRemainingDuration;
int mTimeOfDay;
std::vector<unsigned char> mIdle;
bool mRepeat;
const int mTimeOfDay;
const std::vector<unsigned char> mIdle;
bool mStoredInitialActorPosition;
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell

@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc)
{
int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy);
float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy);
static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)

@ -2128,7 +2128,7 @@ void CharacterController::update(float duration, bool animationOnly)
cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true);
}
const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);
const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);
if (healthLost > (acrobaticsSkill * fatigueTerm))
{
if (!godmode)

@ -101,7 +101,7 @@ namespace MWMechanics
blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat();
blockerTerm *= blockerStats.getFatigueTerm();
int attackerSkill = 0;
float attackerSkill = 0;
if (weapon.isEmpty())
attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand);
else

@ -126,7 +126,7 @@ namespace MWMechanics
return mMagicEffects;
}
void CreatureStats::setAttribute(int index, int base)
void CreatureStats::setAttribute(int index, float base)
{
AttributeValue current = getAttribute(index);
current.setBase(base);
@ -152,10 +152,10 @@ namespace MWMechanics
index == ESM::Attribute::Agility ||
index == ESM::Attribute::Endurance)
{
int strength = getAttribute(ESM::Attribute::Strength).getModified();
int willpower = getAttribute(ESM::Attribute::Willpower).getModified();
int agility = getAttribute(ESM::Attribute::Agility).getModified();
int endurance = getAttribute(ESM::Attribute::Endurance).getModified();
float strength = getAttribute(ESM::Attribute::Strength).getModified();
float willpower = getAttribute(ESM::Attribute::Willpower).getModified();
float agility = getAttribute(ESM::Attribute::Agility).getModified();
float endurance = getAttribute(ESM::Attribute::Endurance).getModified();
DynamicStat<float> fatigue = getFatigue();
float diff = (strength+willpower+agility+endurance) - fatigue.getBase();
float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0;

@ -138,7 +138,7 @@ namespace MWMechanics
void setAttribute(int index, const AttributeValue &value);
// Shortcut to set only the base
void setAttribute(int index, int base);
void setAttribute(int index, float base);
void setHealth(const DynamicStat<float> &value);

@ -688,10 +688,10 @@ namespace MWMechanics
// I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here,
// otherwise one would get different prices when exiting and re-entering the dialogue window...
int clampedDisposition = getDerivedDisposition(ptr);
float a = static_cast<float>(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100));
float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f);
float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float d = static_cast<float>(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100));
float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f);
float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm();
@ -1621,8 +1621,8 @@ namespace MWMechanics
static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0;
if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{
@ -1645,10 +1645,10 @@ namespace MWMechanics
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude();
int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak);
float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak);
float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind;

@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_,
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook)
{
int base = getSkill (skillIndex).getBase();
float base = getSkill (skillIndex).getBase();
if (base >= 100)
if (base >= 100.f)
return;
base += 1;
@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWindowManager()->playSound("skillraise");
std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "");
message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), base);
message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast<int>(base));
if (readBook)
message = "#{sBookSkillMessage}\n" + message;
@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp()
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0;
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
// "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
@ -316,8 +316,8 @@ void MWMechanics::NpcStats::levelUp()
void MWMechanics::NpcStats::updateHealth()
{
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const float strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(floor(0.5f * (strength + endurance)));
}

@ -22,8 +22,8 @@ namespace MWMechanics
float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)
{
NpcStats& stats = ptr.getClass().getNpcStats(ptr);
float agility = static_cast<float>(stats.getAttribute(ESM::Attribute::Agility).getModified());
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm();
}

@ -32,9 +32,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
float fatigueTerm = stats.getFatigueTerm();
int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fRepairAmountMult")->mValue.getFloat();

@ -19,8 +19,8 @@ namespace MWMechanics
: mActor(actor)
{
CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
mAgility = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Agility).getModified());
mLuck = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Luck).getModified());
mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified();
mSecuritySkill = static_cast<float>(actor.getClass().getSkill(actor, ESM::Skill::Security));
mFatigueTerm = creatureStats.getFatigueTerm();
}

@ -40,8 +40,8 @@ namespace MWMechanics
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa

@ -94,8 +94,8 @@ namespace MWMechanics
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);

@ -227,29 +227,29 @@ namespace MWMechanics
}
AttributeValue::AttributeValue() :
mBase(0), mModifier(0), mDamage(0)
mBase(0.f), mModifier(0.f), mDamage(0.f)
{
}
int AttributeValue::getModified() const
float AttributeValue::getModified() const
{
return std::max(0, mBase - (int) mDamage + mModifier);
return std::max(0.f, mBase - mDamage + mModifier);
}
int AttributeValue::getBase() const
float AttributeValue::getBase() const
{
return mBase;
}
int AttributeValue::getModifier() const
float AttributeValue::getModifier() const
{
return mModifier;
}
void AttributeValue::setBase(int base)
void AttributeValue::setBase(float base)
{
mBase = base;
}
void AttributeValue::setModifier(int mod)
void AttributeValue::setModifier(float mod)
{
mModifier = mod;
}
@ -275,14 +275,14 @@ namespace MWMechanics
return mDamage;
}
void AttributeValue::writeState (ESM::StatState<int>& state) const
void AttributeValue::writeState (ESM::StatState<float>& state) const
{
state.mBase = mBase;
state.mMod = mModifier;
state.mDamage = mDamage;
}
void AttributeValue::readState (const ESM::StatState<int>& state)
void AttributeValue::readState (const ESM::StatState<float>& state)
{
mBase = state.mBase;
mModifier = state.mMod;
@ -303,13 +303,13 @@ namespace MWMechanics
mProgress = progress;
}
void SkillValue::writeState (ESM::StatState<int>& state) const
void SkillValue::writeState (ESM::StatState<float>& state) const
{
AttributeValue::writeState (state);
state.mProgress = mProgress;
}
void SkillValue::readState (const ESM::StatState<int>& state)
void SkillValue::readState (const ESM::StatState<float>& state)
{
AttributeValue::readState (state);
mProgress = state.mProgress;

@ -122,20 +122,20 @@ namespace MWMechanics
class AttributeValue
{
int mBase;
int mModifier;
float mBase;
float mModifier;
float mDamage; // needs to be float to allow continuous damage
public:
AttributeValue();
int getModified() const;
int getBase() const;
int getModifier() const;
float getModified() const;
float getBase() const;
float getModifier() const;
void setBase(int base);
void setBase(float base);
void setModifier(int mod);
void setModifier(float mod);
// Maximum attribute damage is limited to the modified value.
// Note: I think MW applies damage directly to mModified, since you can also
@ -145,8 +145,8 @@ namespace MWMechanics
float getDamage() const;
void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state);
void writeState (ESM::StatState<float>& state) const;
void readState (const ESM::StatState<float>& state);
};
class SkillValue : public AttributeValue
@ -157,8 +157,8 @@ namespace MWMechanics
float getProgress() const;
void setProgress(float progress);
void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state);
void writeState (ESM::StatState<float>& state) const;
void readState (const ESM::StatState<float>& state);
};
inline bool operator== (const AttributeValue& left, const AttributeValue& right)

@ -8,6 +8,16 @@ namespace MWMechanics
template <class T>
struct TypedAiPackage : public AiPackage
{
TypedAiPackage() :
AiPackage(T::getTypeId(), T::makeDefaultOptions()) {}
TypedAiPackage(const Options& options) :
AiPackage(T::getTypeId(), options) {}
template <class Derived>
TypedAiPackage(Derived*) :
AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {}
virtual std::unique_ptr<AiPackage> clone() const override
{
return std::make_unique<T>(*static_cast<const T*>(this));

@ -0,0 +1,104 @@
#include "fogmanager.hpp"
#include <algorithm>
#include <components/esm/loadcell.hpp>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp>
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
namespace MWRender
{
FogManager::FogManager()
: mLandFogStart(0.f)
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mFogColor(osg::Vec4f())
, mDistantFog(Settings::Manager::getBool("use distant fog", "Fog"))
, mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor"))
, mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog"))
{
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog");
DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog");
DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog");
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
}
void FogManager::configure(float viewDistance, const ESM::Cell *cell)
{
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog);
if (mDistantFog)
{
float density = std::max(0.2f, cell->mAmbi.mFogDensity);
mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density;
mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
mFogColor = color;
}
else
configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
}
void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{
if (mDistantFog)
{
mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd);
mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
}
else
{
if (fogDepth == 0.0)
{
mLandFogStart = 0.0f;
mLandFogEnd = std::numeric_limits<float>::max();
}
else
{
mLandFogStart = viewDistance * (1 - fogDepth);
mLandFogEnd = viewDistance;
}
mUnderwaterFogStart = std::min(viewDistance, 6666.f) * (1 - underwaterFog);
mUnderwaterFogEnd = std::min(viewDistance, 6666.f);
}
mFogColor = color;
}
float FogManager::getFogStart(bool isUnderwater) const
{
return isUnderwater ? mUnderwaterFogStart : mLandFogStart;
}
float FogManager::getFogEnd(bool isUnderwater) const
{
return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd;
}
osg::Vec4f FogManager::getFogColor(bool isUnderwater) const
{
if (isUnderwater)
{
return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight);
}
return mFogColor;
}
}

@ -0,0 +1,39 @@
#ifndef OPENMW_MWRENDER_FOGMANAGER_H
#define OPENMW_MWRENDER_FOGMANAGER_H
#include <osg/Vec4f>
namespace ESM
{
struct Cell;
}
namespace MWRender
{
class FogManager
{
public:
FogManager();
void configure(float viewDistance, const ESM::Cell *cell);
void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color);
osg::Vec4f getFogColor(bool isUnderwater) const;
float getFogStart(bool isUnderwater) const;
float getFogEnd(bool isUnderwater) const;
private:
float mLandFogStart;
float mLandFogEnd;
float mUnderwaterFogStart;
float mUnderwaterFogEnd;
osg::Vec4f mFogColor;
bool mDistantFog;
osg::Vec4f mUnderwaterColor;
float mUnderwaterWeight;
float mUnderwaterIndoorFog;
};
}
#endif

@ -69,16 +69,7 @@
#include "navmesh.hpp"
#include "actorspaths.hpp"
#include "recastmesh.hpp"
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
#include "fogmanager.hpp"
namespace MWRender
{
@ -204,19 +195,9 @@ namespace MWRender
, mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mNavigator(navigator)
, mLandFogStart(0.f)
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor"))
, mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog"))
, mNightEyeFactor(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
, mBorders(false)
{
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
@ -284,16 +265,6 @@ namespace MWRender
mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog");
DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog");
DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog");
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
mDistantFog = Settings::Manager::getBool("use distant fog", "Fog");
mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders");
const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders");
@ -302,7 +273,7 @@ namespace MWRender
mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps);
if (mDistantTerrain)
if (Settings::Manager::getBool("distant terrain", "Terrain"))
{
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
@ -349,8 +320,9 @@ namespace MWRender
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
mFog.reset(new FogManager());
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
mSky->setCamera(mViewer->getCamera());
mSky->setRainIntensityUniform(mWater->getRainIntensityUniform());
@ -558,9 +530,9 @@ namespace MWRender
bool RenderingManager::toggleBorders()
{
mBorders = !mBorders;
mTerrain->setBordersVisible(mBorders);
return mBorders;
bool borders = !mTerrain->getBordersVisible();
mTerrain->setBordersVisible(borders);
return borders;
}
bool RenderingManager::toggleRenderMode(RenderMode mode)
@ -606,46 +578,12 @@ namespace MWRender
void RenderingManager::configureFog(const ESM::Cell *cell)
{
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog);
if(mDistantFog)
{
float density = std::max(0.2f, cell->mAmbi.mFogDensity);
mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density);
mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
mFogColor = color;
}
else
configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
mFog->configure(mViewDistance, cell);
}
void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{
if(mDistantFog)
{
mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd);
mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
}
else
{
if(fogDepth == 0.0)
{
mLandFogStart = 0.0f;
mLandFogEnd = std::numeric_limits<float>::max();
}
else
{
mLandFogStart = mViewDistance * (1 - fogDepth);
mLandFogEnd = mViewDistance;
}
mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog);
mUnderwaterFogEnd = std::min(mViewDistance, 6666.f);
}
mFogColor = color;
mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
}
SkyManager* RenderingManager::getSkyManager()
@ -674,19 +612,11 @@ namespace MWRender
osg::Vec3f focal, cameraPos;
mCamera->getPosition(focal, cameraPos);
mCurrentCameraPos = cameraPos;
if (mWater->isUnderwater(cameraPos))
{
setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight));
mStateUpdater->setFogStart(mUnderwaterFogStart);
mStateUpdater->setFogEnd(mUnderwaterFogEnd);
}
else
{
setFogColor(mFogColor);
mStateUpdater->setFogStart(mLandFogStart);
mStateUpdater->setFogEnd(mLandFogEnd);
}
bool isUnderwater = mWater->isUnderwater(cameraPos);
mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
setFogColor(mFog->getFogColor(isUnderwater));
}
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
@ -1335,7 +1265,7 @@ namespace MWRender
else if (it->first == "Camera" && it->second == "viewing distance")
{
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
if(!mDistantFog)
if(!Settings::Manager::getBool("use distant fog", "Fog"))
mStateUpdater->setFogEnd(mViewDistance);
updateProjectionMatrix();
}

@ -73,6 +73,7 @@ namespace MWRender
class StateUpdater;
class EffectManager;
class FogManager;
class SkyManager;
class NpcAnimation;
class Pathgrid;
@ -275,6 +276,7 @@ namespace MWRender
std::unique_ptr<Terrain::World> mTerrain;
TerrainStorage* mTerrainStorage;
std::unique_ptr<SkyManager> mSky;
std::unique_ptr<FogManager> mFog;
std::unique_ptr<EffectManager> mEffectManager;
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
@ -284,27 +286,15 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater;
float mLandFogStart;
float mLandFogEnd;
float mUnderwaterFogStart;
float mUnderwaterFogEnd;
osg::Vec4f mUnderwaterColor;
float mUnderwaterWeight;
float mUnderwaterIndoorFog;
osg::Vec4f mFogColor;
osg::Vec4f mAmbientColor;
float mNightEyeFactor;
float mNearClip;
float mViewDistance;
bool mDistantFog : 1;
bool mDistantTerrain : 1;
bool mFieldOfViewOverridden : 1;
bool mFieldOfViewOverridden;
float mFieldOfViewOverride;
float mFieldOfView;
float mFirstPersonFieldOfView;
bool mBorders;
void operator = (const RenderingManager&);
RenderingManager(const RenderingManager&);

@ -95,7 +95,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value =
Interpreter::Type_Float value =
ptr.getClass()
.getCreatureStats (ptr)
.getAttribute(mIndex)
@ -118,7 +118,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger;
Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex);
@ -140,7 +140,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger;
Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass()
@ -155,9 +155,9 @@ namespace MWScript
return;
if (value < 0)
attribute.setBase(std::max(0, attribute.getBase() + value));
attribute.setBase(std::max(0.f, attribute.getBase() + value));
else
attribute.setBase(std::min(100, attribute.getBase() + value));
attribute.setBase(std::min(100.f, attribute.getBase() + value));
ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute);
}
@ -345,7 +345,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex);
Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex);
runtime.push (value);
}
@ -364,7 +364,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger;
Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop();
MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr);
@ -386,7 +386,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger;
Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop();
MWMechanics::SkillValue &skill = ptr.getClass()
@ -396,14 +396,14 @@ namespace MWScript
if (value == 0)
return;
if (((skill.getBase() <= 0) && (value < 0))
|| ((skill.getBase() >= 100) && (value > 0)))
if (((skill.getBase() <= 0.f) && (value < 0.f))
|| ((skill.getBase() >= 100.f) && (value > 0.f)))
return;
if (value < 0)
skill.setBase(std::max(0, skill.getBase() + value));
skill.setBase(std::max(0.f, skill.getBase() + value));
else
skill.setBase(std::min(100, skill.getBase() + value));
skill.setBase(std::min(100.f, skill.getBase() + value));
}
};

@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
profile.mPlayerClassId = classId;
profile.mPlayerCell = world.getCellName();
profile.mInGameTime.mGameHour = world.getTimeStamp().getHour();
profile.mInGameTime.mDay = world.getDay();
profile.mInGameTime.mMonth = world.getMonth();
profile.mInGameTime.mYear = world.getYear();
profile.mInGameTime = world.getEpochTimeStamp();
profile.mTimePlayed = mTimePlayed;
profile.mDescription = description;

@ -421,7 +421,7 @@ namespace MWWorld
return canSwim(ptr) || canWalk(ptr) || canFly(ptr);
}
int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const
float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const
{
throw std::runtime_error("class does not support skills");
}

@ -321,7 +321,7 @@ namespace MWWorld
bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
bool isMobile(const MWWorld::Ptr& ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;

@ -0,0 +1,227 @@
#include "datetimemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "esmstore.hpp"
#include "globals.hpp"
#include "timestamp.hpp"
namespace
{
static int getDaysPerMonth(int month)
{
switch (month)
{
case 0: return 31;
case 1: return 28;
case 2: return 31;
case 3: return 30;
case 4: return 31;
case 5: return 30;
case 6: return 31;
case 7: return 31;
case 8: return 30;
case 9: return 31;
case 10: return 30;
case 11: return 31;
}
throw std::runtime_error ("month out of range");
}
}
namespace MWWorld
{
void DateTimeManager::setup(Globals& globalVariables)
{
mGameHour = globalVariables["gamehour"].getFloat();
mDaysPassed = globalVariables["dayspassed"].getInteger();
mDay = globalVariables["day"].getInteger();
mMonth = globalVariables["month"].getInteger();
mYear = globalVariables["year"].getInteger();
mTimeScale = globalVariables["timescale"].getFloat();
}
void DateTimeManager::setHour(double hour)
{
if (hour < 0)
hour = 0;
int days = static_cast<int>(hour / 24);
hour = std::fmod(hour, 24);
mGameHour = static_cast<float>(hour);
if (days > 0)
setDay(days + mDay);
}
void DateTimeManager::setDay(int day)
{
if (day < 1)
day = 1;
int month = mMonth;
while (true)
{
int days = getDaysPerMonth(month);
if (day <= days)
break;
if (month < 11)
{
++month;
}
else
{
month = 0;
mYear++;
}
day -= days;
}
mDay = day;
mMonth = month;
}
TimeStamp DateTimeManager::getTimeStamp() const
{
return TimeStamp(mGameHour, mDaysPassed);
}
float DateTimeManager::getTimeScaleFactor() const
{
return mTimeScale;
}
ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const
{
ESM::EpochTimeStamp timeStamp;
timeStamp.mGameHour = mGameHour;
timeStamp.mDay = mDay;
timeStamp.mMonth = mMonth;
timeStamp.mYear = mYear;
return timeStamp;
}
void DateTimeManager::setMonth(int month)
{
if (month < 0)
month = 0;
int years = month / 12;
month = month % 12;
int days = getDaysPerMonth(month);
if (mDay > days)
mDay = days;
mMonth = month;
if (years > 0)
mYear += years;
}
void DateTimeManager::advanceTime(double hours, Globals& globalVariables)
{
hours += mGameHour;
setHour(hours);
int days = static_cast<int>(hours / 24);
if (days > 0)
mDaysPassed += days;
globalVariables["gamehour"].setFloat(mGameHour);
globalVariables["dayspassed"].setInteger(mDaysPassed);
globalVariables["day"].setInteger(mDay);
globalVariables["month"].setInteger(mMonth);
globalVariables["year"].setInteger(mYear);
}
std::string DateTimeManager::getMonthName(int month) const
{
if (month == -1)
month = mMonth;
const int months = 12;
if (month < 0 || month >= months)
return std::string();
static const char *monthNames[months] =
{
"sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand",
"sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed",
"sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar"
};
const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(monthNames[month]);
return setting->mValue.getString();
}
bool DateTimeManager::updateGlobalFloat(const std::string& name, float value)
{
if (name=="gamehour")
{
setHour(value);
return true;
}
else if (name=="day")
{
setDay(static_cast<int>(value));
return true;
}
else if (name=="month")
{
setMonth(static_cast<int>(value));
return true;
}
else if (name=="year")
{
mYear = static_cast<int>(value);
}
else if (name=="timescale")
{
mTimeScale = value;
}
else if (name=="dayspassed")
{
mDaysPassed = static_cast<int>(value);
}
return false;
}
bool DateTimeManager::updateGlobalInt(const std::string& name, int value)
{
if (name=="gamehour")
{
setHour(static_cast<float>(value));
return true;
}
else if (name=="day")
{
setDay(value);
return true;
}
else if (name=="month")
{
setMonth(value);
return true;
}
else if (name=="year")
{
mYear = value;
}
else if (name=="timescale")
{
mTimeScale = static_cast<float>(value);
}
else if (name=="dayspassed")
{
mDaysPassed = value;
}
return false;
}
}

@ -0,0 +1,43 @@
#ifndef GAME_MWWORLD_DATETIMEMANAGER_H
#define GAME_MWWORLD_DATETIMEMANAGER_H
#include <string>
namespace ESM
{
struct EpochTimeStamp;
}
namespace MWWorld
{
class Globals;
class TimeStamp;
class DateTimeManager
{
int mDaysPassed = 0;
int mDay = 0;
int mMonth = 0;
int mYear = 0;
float mGameHour = 0.f;
float mTimeScale = 0.f;
void setHour(double hour);
void setDay(int day);
void setMonth(int month);
public:
std::string getMonthName(int month) const;
TimeStamp getTimeStamp() const;
ESM::EpochTimeStamp getEpochTimeStamp() const;
float getTimeScaleFactor() const;
void advanceTime(double hours, Globals& globalVariables);
void setup(Globals& globalVariables);
bool updateGlobalInt(const std::string& name, int value);
bool updateGlobalFloat(const std::string& name, float value);
};
}
#endif

@ -2,10 +2,9 @@
#include <stdexcept>
#include <components/misc/stringops.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/misc/stringops.hpp>
#include "esmstore.hpp"

@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
// rate weapon
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
{
int max = 0;
float max = 0;
int maxWeaponSkill = -1;
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{
int skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));
float skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));
if (skillValue > max && !weaponSkillVisited[j])
{
max = skillValue;
@ -399,7 +399,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots&
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter)

@ -387,6 +387,18 @@ namespace MWWorld
{
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
{
// If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame.
MWWorld::Ptr caster = it->getCaster();
if (!caster.isEmpty() && caster.getClass().isActor())
{
if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead())
{
cleanupMagicBolt(*it);
it = mMagicBolts.erase(it);
continue;
}
}
osg::Quat orient = it->mNode->getAttitude();
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fTargetSpellMaxSpeed")->mValue.getFloat();
@ -405,8 +417,6 @@ namespace MWWorld
update(*it, duration);
MWWorld::Ptr caster = it->getCaster();
// 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())

@ -60,6 +60,7 @@
#include "../mwphysics/object.hpp"
#include "../mwphysics/constants.hpp"
#include "datetimemanager.hpp"
#include "player.hpp"
#include "manualref.hpp"
#include "cellstore.hpp"
@ -121,33 +122,11 @@ namespace MWWorld
LoadersContainer mLoaders;
};
int World::getDaysPerMonth (int month) const
{
switch (month)
{
case 0: return 31;
case 1: return 28;
case 2: return 31;
case 3: return 30;
case 4: return 31;
case 5: return 30;
case 6: return 31;
case 7: return 31;
case 8: return 30;
case 9: return 31;
case 10: return 30;
case 11: return 31;
}
throw std::runtime_error ("month out of range");
}
void World::adjustSky()
{
if (mSky && (isCellExterior() || isCellQuasiExterior()))
{
mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger());
updateSkyDate();
mRendering->setSkyEnabled(true);
}
else
@ -193,6 +172,8 @@ namespace MWWorld
if (mEsm[0].getFormat() == 0)
ensureNeededRecords();
mCurrentDate.reset(new DateTimeManager());
fillGlobalVariables();
mStore.setUp(true);
@ -227,13 +208,7 @@ namespace MWWorld
void World::fillGlobalVariables()
{
mGlobalVariables.fill (mStore);
mGameHour = &mGlobalVariables["gamehour"];
mDaysPassed = &mGlobalVariables["dayspassed"];
mDay = &mGlobalVariables["day"];
mMonth = &mGlobalVariables["month"];
mYear = &mGlobalVariables["year"];
mTimeScale = &mGlobalVariables["timescale"];
mCurrentDate->setup(mGlobalVariables);
}
void World::startNewGame (bool bypass)
@ -310,6 +285,7 @@ namespace MWWorld
mPhysics->toggleCollisionMode();
MWBase::Environment::get().getWindowManager()->updatePlayer();
mCurrentDate->setup(mGlobalVariables);
}
void World::clear()
@ -639,26 +615,20 @@ namespace MWWorld
void World::setGlobalInt (const std::string& name, int value)
{
if (name=="gamehour")
setHour (value);
else if (name=="day")
setDay (value);
else if (name=="month")
setMonth (value);
else
bool dateUpdated = mCurrentDate->updateGlobalInt(name, value);
if (dateUpdated)
updateSkyDate();
mGlobalVariables[name].setInteger (value);
}
void World::setGlobalFloat (const std::string& name, float value)
{
if (name=="gamehour")
setHour (value);
else if (name=="day")
setDay(static_cast<int>(value));
else if (name=="month")
setMonth(static_cast<int>(value));
else
mGlobalVariables[name].setFloat (value);
bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value);
if (dateUpdated)
updateSkyDate();
mGlobalVariables[name].setFloat(value);
}
int World::getGlobalInt (const std::string& name) const
@ -676,6 +646,11 @@ namespace MWWorld
return mGlobalVariables.getType (name);
}
std::string World::getMonthName (int month) const
{
return mCurrentDate->getMonthName(month);
}
std::string World::getCellName (const MWWorld::CellStore *cell) const
{
if (!cell)
@ -894,130 +869,29 @@ namespace MWWorld
}
mWeatherManager->advanceTime (hours, incremental);
mCurrentDate->advanceTime(hours, mGlobalVariables);
updateSkyDate();
if (!incremental)
{
mRendering->notifyWorldSpaceChanged();
mProjectileManager->clear();
}
hours += mGameHour->getFloat();
setHour (hours);
int days = static_cast<int>(hours / 24);
if (days>0)
mDaysPassed->setInteger (
days + mDaysPassed->getInteger());
}
void World::setHour (double hour)
{
if (hour<0)
hour = 0;
int days = static_cast<int>(hour / 24);
hour = std::fmod (hour, 24);
mGameHour->setFloat(static_cast<float>(hour));
if (days>0)
setDay (days + mDay->getInteger());
}
void World::setDay (int day)
{
if (day<1)
day = 1;
int month = mMonth->getInteger();
while (true)
{
int days = getDaysPerMonth (month);
if (day<=days)
break;
if (month<11)
{
++month;
}
else
{
month = 0;
mYear->setInteger(mYear->getInteger()+1);
}
day -= days;
}
mDay->setInteger(day);
mMonth->setInteger(month);
mRendering->skySetDate(day, month);
}
void World::setMonth (int month)
{
if (month<0)
month = 0;
int years = month / 12;
month = month % 12;
int days = getDaysPerMonth (month);
if (mDay->getInteger()>days)
mDay->setInteger (days);
mMonth->setInteger (month);
if (years>0)
mYear->setInteger (years+mYear->getInteger());
mRendering->skySetDate (mDay->getInteger(), month);
}
int World::getDay() const
{
return mDay->getInteger();
}
int World::getMonth() const
{
return mMonth->getInteger();
}
int World::getYear() const
float World::getTimeScaleFactor() const
{
return mYear->getInteger();
return mCurrentDate->getTimeScaleFactor();
}
std::string World::getMonthName (int month) const
{
if (month==-1)
month = getMonth();
const int months = 12;
if (month<0 || month>=months)
return "";
static const char *monthNames[months] =
TimeStamp World::getTimeStamp() const
{
"sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand",
"sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed",
"sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar"
};
return mStore.get<ESM::GameSetting>().find (monthNames[month])->mValue.getString();
return mCurrentDate->getTimeStamp();
}
TimeStamp World::getTimeStamp() const
ESM::EpochTimeStamp World::getEpochTimeStamp() const
{
return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger());
return mCurrentDate->getEpochTimeStamp();
}
bool World::toggleSky()
@ -1042,11 +916,6 @@ namespace MWWorld
mRendering->skySetMoonColour (red);
}
float World::getTimeScaleFactor() const
{
return mTimeScale->getFloat();
}
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{
mPhysics->clearQueuedMovement();
@ -1089,6 +958,8 @@ namespace MWWorld
changeToExteriorCell (position, adjustPlayerPos, changeEvent);
else
changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent);
mCurrentDate->setup(mGlobalVariables);
}
void World::markCellAsUnchanged()
@ -3968,4 +3839,10 @@ namespace MWWorld
mNavigator->reportStats(frameNumber, stats);
mPhysics->reportStats(frameNumber, stats);
}
void World::updateSkyDate()
{
ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp();
mRendering->skySetDate(currentDate.mDay, currentDate.mMonth);
}
}

@ -69,6 +69,7 @@ namespace MWPhysics
namespace MWWorld
{
class DateTimeManager;
class WeatherManager;
class Player;
class ProjectileManager;
@ -85,13 +86,6 @@ namespace MWWorld
LocalScripts mLocalScripts;
MWWorld::Globals mGlobalVariables;
ESM::Variant* mGameHour;
ESM::Variant* mDaysPassed;
ESM::Variant* mDay;
ESM::Variant* mMonth;
ESM::Variant* mYear;
ESM::Variant* mTimeScale;
Cells mCells;
std::string mCurrentWorldSpace;
@ -102,6 +96,7 @@ namespace MWWorld
std::unique_ptr<MWRender::RenderingManager> mRendering;
std::unique_ptr<MWWorld::Scene> mWorldScene;
std::unique_ptr<MWWorld::WeatherManager> mWeatherManager;
std::unique_ptr<MWWorld::DateTimeManager> mCurrentDate;
std::shared_ptr<ProjectileManager> mProjectileManager;
bool mSky;
@ -139,7 +134,6 @@ namespace MWWorld
World& operator= (const World&);
void updateWeather(float duration, bool paused = false);
int getDaysPerMonth (int month) const;
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
@ -173,6 +167,8 @@ namespace MWWorld
void fillGlobalVariables();
void updateSkyDate();
/**
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
* @param fileCollections- Container which holds content file names and their paths
@ -318,24 +314,14 @@ namespace MWWorld
void advanceTime (double hours, bool incremental = false) override;
///< Advance in-game time.
void setHour (double hour) override;
///< Set in-game time hour.
void setMonth (int month) override;
///< Set in-game time month.
void setDay (int day) override;
///< Set in-game time day.
int getDay() const override;
int getMonth() const override;
int getYear() const override;
std::string getMonthName (int month = -1) const override;
///< Return name of month (-1: current month)
TimeStamp getTimeStamp() const override;
///< Return current in-game time stamp.
///< Return current in-game time and number of day since new game start.
ESM::EpochTimeStamp getEpochTimeStamp() const override;
///< Return current in-game date and time.
bool toggleSky() override;
///< \return Resulting mode

@ -287,5 +287,5 @@ endif()
set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE)
if (BULLET_USE_DOUBLES)
add_definitions(-DBT_USE_DOUBLE_PRECISION)
target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION)
endif()

@ -247,8 +247,8 @@ namespace Compiler
extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit);
extensions.registerInstruction ("stopscript", "c", opcodeStopScript);
extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed);
extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit);
extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit);
extensions.registerInstruction ("enable", "x", opcodeEnable, opcodeEnableExplicit);
extensions.registerInstruction ("disable", "x", opcodeDisable, opcodeDisableExplicit);
extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit);
extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit);
@ -423,13 +423,13 @@ namespace Compiler
for (int i=0; i<numberOfAttributes; ++i)
{
extensions.registerFunction (get + attributes[i], 'l', "",
extensions.registerFunction (get + attributes[i], 'f', "",
opcodeGetAttribute+i, opcodeGetAttributeExplicit+i);
extensions.registerInstruction (set + attributes[i], "l",
extensions.registerInstruction (set + attributes[i], "f",
opcodeSetAttribute+i, opcodeSetAttributeExplicit+i);
extensions.registerInstruction (mod + attributes[i], "l",
extensions.registerInstruction (mod + attributes[i], "f",
opcodeModAttribute+i, opcodeModAttributeExplicit+i);
}
@ -453,13 +453,13 @@ namespace Compiler
for (int i=0; i<numberOfSkills; ++i)
{
extensions.registerFunction (get + skills[i], 'l', "",
extensions.registerFunction (get + skills[i], 'f', "",
opcodeGetSkill+i, opcodeGetSkillExplicit+i);
extensions.registerInstruction (set + skills[i], "l",
extensions.registerInstruction (set + skills[i], "f",
opcodeSetSkill+i, opcodeSetSkillExplicit+i);
extensions.registerInstruction (mod + skills[i], "l",
extensions.registerInstruction (mod + skills[i], "f",
opcodeModSkill+i, opcodeModSkillExplicit+i);
}

@ -86,13 +86,6 @@ namespace Compiler
bool LineParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
if (mState==PotentialEndState)
{
getErrorHandler().warning ("Stray string argument", loc);
mState = EndState;
return true;
}
if (mState==SetState)
{
std::string name2 = Misc::StringUtils::lowerCase (name);
@ -445,8 +438,7 @@ namespace Compiler
return true;
}
if (code==Scanner::S_newline &&
(mState==EndState || mState==BeginState || mState==PotentialEndState))
if (code==Scanner::S_newline && (mState==EndState || mState==BeginState))
return false;
if (code==Scanner::S_comma && mState==MessageState)

@ -25,8 +25,7 @@ namespace Compiler
SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState,
SetMemberVarState, SetMemberVarState2,
MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState,
EndState, PotentialEndState /* may have a stray string argument */,
PotentialExplicitState, ExplicitState, MemberState
EndState, PotentialExplicitState, ExplicitState, MemberState
};
Locals& mLocals;

@ -8,18 +8,18 @@ namespace ESM
{
// format 0, saved games only
struct ContainerState : public ObjectState
struct ContainerState final : public ObjectState
{
InventoryState mInventory;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
void load (ESMReader &esm) final;
void save (ESMWriter &esm, bool inInventory = false) const final;
virtual ContainerState& asContainerState()
ContainerState& asContainerState() final
{
return *this;
}
virtual const ContainerState& asContainerState() const
const ContainerState& asContainerState() const final
{
return *this;
}

@ -7,19 +7,19 @@ namespace ESM
{
// format 0, saved games only
struct CreatureLevListState : public ObjectState
struct CreatureLevListState final : public ObjectState
{
int mSpawnActorId;
bool mSpawn;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
void load (ESMReader &esm) final;
void save (ESMWriter &esm, bool inInventory = false) const final;
virtual CreatureLevListState& asCreatureLevListState()
CreatureLevListState& asCreatureLevListState() final
{
return *this;
}
virtual const CreatureLevListState& asCreatureLevListState() const
const CreatureLevListState& asCreatureLevListState() const final
{
return *this;
}

@ -9,7 +9,7 @@ namespace ESM
{
// format 0, saved games only
struct CreatureState : public ObjectState
struct CreatureState final : public ObjectState
{
InventoryState mInventory;
CreatureStats mCreatureStats;
@ -17,14 +17,14 @@ namespace ESM
/// Initialize to default state
void blank();
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
void load (ESMReader &esm) final;
void save (ESMWriter &esm, bool inInventory = false) const final;
virtual CreatureState& asCreatureState()
CreatureState& asCreatureState() final
{
return *this;
}
virtual const CreatureState& asCreatureState() const
const CreatureState& asCreatureState() const final
{
return *this;
}

@ -4,8 +4,9 @@
void ESM::CreatureStats::load (ESMReader &esm)
{
bool intFallback = esm.getFormat() < 11;
for (int i=0; i<8; ++i)
mAttributes[i].load (esm);
mAttributes[i].load (esm, intFallback);
for (int i=0; i<3; ++i)
mDynamic[i].load (esm);

@ -29,7 +29,7 @@ namespace ESM
TimeStamp mNextWorsening;
};
StatState<int> mAttributes[Attribute::Length];
StatState<float> mAttributes[Attribute::Length];
StatState<float> mDynamic[3];
MagicEffects mMagicEffects;

@ -14,6 +14,14 @@ struct TimeStamp
int mDay;
};
struct EpochTimeStamp
{
float mGameHour;
int mDay;
int mMonth;
int mYear;
};
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
typedef uint32_t Color;
@ -49,6 +57,26 @@ struct Position
};
#pragma pack(pop)
bool inline operator== (const Position& left, const Position& right) noexcept
{
return left.pos[0] == right.pos[0] &&
left.pos[1] == right.pos[1] &&
left.pos[2] == right.pos[2] &&
left.rot[0] == right.rot[0] &&
left.rot[1] == right.rot[1] &&
left.rot[2] == right.rot[2];
}
bool inline operator!= (const Position& left, const Position& right) noexcept
{
return left.pos[0] != right.pos[0] ||
left.pos[1] != right.pos[1] ||
left.pos[2] != right.pos[2] ||
left.rot[0] != right.rot[0] ||
left.rot[1] != right.rot[1] ||
left.rot[2] != right.rot[2];
}
template <int a, int b, int c, int d>
struct FourCC
{

@ -7,18 +7,18 @@ namespace ESM
{
// format 0, saved games only
struct DoorState : public ObjectState
struct DoorState final : public ObjectState
{
int mDoorState = 0;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
void load (ESMReader &esm) final;
void save (ESMWriter &esm, bool inInventory = false) const final;
virtual DoorState& asDoorState()
DoorState& asDoorState() final
{
return *this;
}
virtual const DoorState& asDoorState() const
const DoorState& asDoorState() const final
{
return *this;
}

@ -10,7 +10,7 @@ namespace ESM
{
// format 0, saved games only
struct NpcState : public ObjectState
struct NpcState final : public ObjectState
{
InventoryState mInventory;
NpcStats mNpcStats;
@ -19,14 +19,14 @@ namespace ESM
/// Initialize to default state
void blank();
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
void load (ESMReader &esm) final;
void save (ESMWriter &esm, bool inInventory = false) const final;
virtual NpcState& asNpcState()
NpcState& asNpcState() final
{
return *this;
}
virtual const NpcState& asNpcState() const
const NpcState& asNpcState() const final
{
return *this;
}

@ -31,8 +31,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mDisposition = 0;
esm.getHNOT (mDisposition, "DISP");
bool intFallback = esm.getFormat() < 11;
for (int i=0; i<27; ++i)
mSkills[i].load (esm);
mSkills[i].load (esm, intFallback);
mWerewolfDeprecatedData = false;
if (esm.getFormat() < 8 && esm.peekNextSub("STBA"))
@ -40,17 +41,17 @@ void ESM::NpcStats::load (ESMReader &esm)
// we have deprecated werewolf skills, stored interleaved
// Load into one big vector, then remove every 2nd value
mWerewolfDeprecatedData = true;
std::vector<ESM::StatState<int> > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0]));
std::vector<ESM::StatState<float> > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0]));
for (int i=0; i<27; ++i)
{
ESM::StatState<int> skill;
skill.load(esm);
ESM::StatState<float> skill;
skill.load(esm, intFallback);
skills.push_back(skill);
}
int i=0;
for (std::vector<ESM::StatState<int> >::iterator it = skills.begin(); it != skills.end(); ++i)
for (std::vector<ESM::StatState<float> >::iterator it = skills.begin(); it != skills.end(); ++i)
{
if (i%2 == 1)
it = skills.erase(it);
@ -68,7 +69,7 @@ void ESM::NpcStats::load (ESMReader &esm)
{
ESM::StatState<int> dummy;
for (int i=0; i<8; ++i)
dummy.load(esm);
dummy.load(esm, intFallback);
mWerewolfDeprecatedData = true;
}

@ -31,7 +31,7 @@ namespace ESM
std::map<std::string, Faction> mFactions; // lower case IDs
int mDisposition;
StatState<int> mSkills[27];
StatState<float> mSkills[27];
int mBounty;
int mReputation;
int mWerewolfKills;

@ -26,6 +26,7 @@ void ESM::ObjectState::load (ESMReader &esm)
mCount = 1;
esm.getHNOT (mCount, "COUN");
mPosition = mRef.mPos;
esm.getHNOT (mPosition, "POS_", 24);
if (esm.isNextSub("LROT"))
@ -61,7 +62,7 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
if (mCount!=1)
esm.writeHNT ("COUN", mCount);
if (!inInventory)
if (!inInventory && mPosition != mRef.mPos)
esm.writeHNT ("POS_", mPosition, 24);
if (mFlags != 0)

@ -44,12 +44,13 @@ void ESM::Player::load (ESMReader &esm)
checkPrevItems = false;
}
bool intFallback = esm.getFormat() < 11;
if (esm.hasMoreSubs())
{
for (int i=0; i<ESM::Attribute::Length; ++i)
mSaveAttributes[i].load(esm);
mSaveAttributes[i].load(esm, intFallback);
for (int i=0; i<ESM::Skill::Length; ++i)
mSaveSkills[i].load(esm);
mSaveSkills[i].load(esm, intFallback);
}
}

@ -30,8 +30,8 @@ namespace ESM
int mCurrentCrimeId;
int mPaidCrimeId;
StatState<int> mSaveAttributes[ESM::Attribute::Length];
StatState<int> mSaveSkills[ESM::Skill::Length];
StatState<float> mSaveAttributes[ESM::Attribute::Length];
StatState<float> mSaveSkills[ESM::Skill::Length];
typedef std::map<std::string, std::string> PreviousItems; // previous equipped items, needed for bound spells
PreviousItems mPreviousItems;

@ -2,10 +2,9 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 10;
int ESM::SavedGame::sCurrentFormat = 12;
void ESM::SavedGame::load (ESMReader &esm)
{

@ -4,6 +4,8 @@
#include <vector>
#include <string>
#include "defs.hpp"
namespace ESM
{
class ESMReader;
@ -17,14 +19,6 @@ namespace ESM
static int sCurrentFormat;
struct TimeStamp
{
float mGameHour;
int mDay;
int mMonth;
int mYear;
};
std::vector<std::string> mContentFiles;
std::string mPlayerName;
int mPlayerLevel;
@ -36,7 +30,7 @@ namespace ESM
std::string mPlayerClassName;
std::string mPlayerCell;
TimeStamp mInGameTime;
EpochTimeStamp mInGameTime;
double mTimePlayed;
std::string mDescription;
std::vector<char> mScreenshot; // raw jpg-encoded data

@ -9,19 +9,44 @@ namespace ESM
StatState<T>::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {}
template<typename T>
void StatState<T>::load(ESMReader &esm)
void StatState<T>::load(ESMReader &esm, bool intFallback)
{
// We changed stats values from integers to floats; ensure backwards compatibility
if (intFallback)
{
int base = 0;
esm.getHNT(base, "STBA");
mBase = static_cast<float>(base);
int mod = 0;
esm.getHNOT(mod, "STMO");
mMod = static_cast<float>(mod);
int current = 0;
esm.getHNOT(current, "STCU");
mCurrent = static_cast<float>(current);
// mDamage was changed to a float; ensure backwards compatibility
int oldDamage = 0;
esm.getHNOT(oldDamage, "STDA");
mDamage = static_cast<float>(oldDamage);
}
else
{
mBase = 0;
esm.getHNT(mBase, "STBA");
mMod = 0;
esm.getHNOT(mMod, "STMO");
mCurrent = 0;
esm.getHNOT(mCurrent, "STCU");
// mDamage was changed to a float; ensure backwards compatibility
T oldDamage = 0;
esm.getHNOT(oldDamage, "STDA");
mDamage = static_cast<float>(oldDamage);
mDamage = 0;
esm.getHNOT(mDamage, "STDF");
mProgress = 0;
}
esm.getHNOT(mDamage, "STDF");

@ -20,7 +20,7 @@ namespace ESM
StatState();
void load (ESMReader &esm);
void load (ESMReader &esm, bool intFallback = false);
void save (ESMWriter &esm) const;
};
}

@ -139,6 +139,7 @@ namespace Terrain
virtual void enable(bool enabled) {}
virtual void setBordersVisible(bool visible);
virtual bool getBordersVisible() { return mBorderVisible; }
/// Create a View to use with preload feature. The caller is responsible for deleting the view.
/// @note Thread safe.

Loading…
Cancel
Save