Merge branch 'master' into automove

pull/2892/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 #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly Bug #3676: NiParticleColorModifier isn't applied properly
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects 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 #4623: Corprus implementation is incorrect
Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5108: Savegame bloating due to inefficient fog textures format
@ -25,6 +26,7 @@
Bug #5427: GetDistance unknown ID error is misleading Bug #5427: GetDistance unknown ID error is misleading
Bug #5435: Enemies can't hurt the player when collision is off 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 #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 Bug #5452: Autowalk is being included in savegames
Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines Feature #5445: Handle NiLines
@ -299,7 +301,7 @@
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window 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 #5201: Editor: Show tool outline in scene view, when using editmodes
Feature #5219: Impelement TestCells console command Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape 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 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 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 if [ $MISSINGTOOLS -ne 0 ]; then
wrappedExit 1 wrappedExit 1
@ -745,6 +754,11 @@ fi
if [ -d 'Qt/5.15.0' ]; then if [ -d 'Qt/5.15.0' ]; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then 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 pushd "$DEPS" > /dev/null
if ! [ -d 'aqt-venv' ]; then if ! [ -d 'aqt-venv' ]; then
echo " Creating Virtualenv for aqt..." echo " Creating Virtualenv for aqt..."

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

@ -48,6 +48,7 @@ namespace ESM
struct EffectList; struct EffectList;
struct CreatureLevList; struct CreatureLevList;
struct ItemLevList; struct ItemLevList;
struct TimeStamp;
} }
namespace MWRender namespace MWRender
@ -204,24 +205,14 @@ namespace MWBase
virtual void advanceTime (double hours, bool incremental = false) = 0; virtual void advanceTime (double hours, bool incremental = false) = 0;
///< Advance in-game time. ///< 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; virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month) ///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0; 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; virtual bool toggleSky() = 0;
///< \return Resulting mode ///< \return Resulting mode

@ -276,7 +276,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
int armorSkillType = getEquipmentSkill(ptr); 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(); const MWBase::World *world = MWBase::Environment::get().getWorld();
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger(); 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 float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); 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 int Creature::getServices(const MWWorld::ConstPtr &actor) const
@ -745,7 +745,7 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); 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 = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();

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

@ -125,7 +125,7 @@ namespace MWClass
} }
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); 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 = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();

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

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

@ -95,9 +95,9 @@ namespace MWGui
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) 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 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>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

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

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

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

@ -74,11 +74,11 @@ namespace MWGui
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
// NPC can train you in his best 3 skills // 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) 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)); skills.push_back(std::make_pair(i, value));
} }

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

@ -132,7 +132,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); 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; health = 0.1f * endurance;
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
@ -765,7 +765,7 @@ namespace MWMechanics
{ {
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); 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; float base = 1.f;
if (ptr == getPlayer()) if (ptr == getPlayer())
@ -844,7 +844,7 @@ namespace MWMechanics
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->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); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
@ -871,7 +871,7 @@ namespace MWMechanics
return; return;
// Restore fatigue // 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>(); 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 fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->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) 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()) if (iter->first.getClass().getCreatureStats(iter->first).isDead())
continue; continue;

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

@ -29,12 +29,13 @@ namespace MWMechanics
AiActivate(const ESM::AiSequence::AiActivate* activate); AiActivate(const ESM::AiSequence::AiActivate* activate);
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; 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; void writeState(ESM::AiSequence::AiSequence& sequence) const final;
private: private:
std::string mObjectId; const std::string mObjectId;
}; };
} }
#endif // GAME_MWMECHANICS_AIACTIVATE_H #endif // GAME_MWMECHANICS_AIACTIVATE_H

@ -72,16 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return false; 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 bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{ {
return (actorPos - mLastPos).length2() < 10 * 10; 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; 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; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
float mDuration; float mDuration;
MWWorld::ConstPtr mDoorPtr; const MWWorld::ConstPtr mDoorPtr;
osg::Vec3f mLastPos; osg::Vec3f mLastPos;
int mDirection; int mDirection;

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

@ -12,13 +12,16 @@ namespace MWMechanics
public: public:
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; 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; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
}; };
} }
#endif #endif

@ -10,12 +10,22 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
namespace MWMechanics
{
namespace
{
float getInitialDistance(const std::string& spellId)
{
ActionSpell action = ActionSpell(spellId);
bool isRanged;
return action.getCombatRange(isRanged);
}
}
}
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0) : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId))
{ {
ActionSpell action = ActionSpell(spellId);
bool isRanged;
mDistance = action.getCombatRange(isRanged);
} }
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) 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; 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; 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; MWWorld::Ptr getTarget() const final;
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 3;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
std::string mTargetId; const std::string mTargetId;
std::string mSpellId; const std::string mSpellId;
bool mCasting; bool mCasting;
bool mManual; const bool mManual;
float mDistance; 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 MWWorld::Ptr AiCombat::getTarget() const
{ {
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); 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; 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 ///Returns target ID
MWWorld::Ptr getTarget() const final; MWWorld::Ptr getTarget() const final;
void writeState(ESM::AiSequence::AiSequence &sequence) const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
private: private:
/// Returns true if combat should end /// Returns true if combat should end
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); 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()) , mCellY(std::numeric_limits<int>::max())
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
mMaxDist = 450;
} }
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z) 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()) , mCellY(std::numeric_limits<int>::max())
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
mMaxDist = 450;
} }
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
: mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) : 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) , mRemainingDuration(escort->mRemainingDuration)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
mTargetActorRefId = escort->mTargetId; mTargetActorRefId = escort->mTargetId;
mTargetActorId = escort->mTargetActorId; 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) bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -100,11 +95,6 @@ namespace MWMechanics
return false; return false;
} }
int AiEscort::getTypeId() const
{
return TypeIdEscort;
}
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort()); 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; 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; } static constexpr Options makeDefaultOptions()
{
bool sideWithTarget() const final { return true; } AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mSideWithTarget = true;
return options;
}
void writeState(ESM::AiSequence::AiSequence &sequence) const final; 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); } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private: private:
std::string mCellId; const std::string mCellId;
float mX; const float mX;
float mY; const float mY;
float mZ; const float mZ;
float mMaxDist; float mMaxDist = 450;
float mDuration; // In hours const float mDuration; // In hours
float mRemainingDuration; // In hours float mRemainingDuration; // In hours
int mCellX; const int mCellX;
int mCellY; const int mCellY;
}; };
} }
#endif #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(); 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)); 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; 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; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
float mTargetX, mTargetY; const float mTargetX;
const float mTargetY;
}; };
} }

@ -16,25 +16,24 @@
namespace MWMechanics namespace MWMechanics
{ {
int AiFollow::mFollowIndexCounter = 0; int AiFollow::mFollowIndexCounter = 0;
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) 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++) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
} }
AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z) 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++) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
} }
AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z) 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++) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actor.getCellRef().getRefId(); 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) 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++) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actor.getCellRef().getRefId(); 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) 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++) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actor.getCellRef().getRefId(); mTargetActorRefId = actor.getCellRef().getRefId();
@ -58,18 +58,18 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
} }
AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) 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) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
, mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++) , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = follow->mTargetId; mTargetActorRefId = follow->mTargetId;
mTargetActorId = follow->mTargetActorId; 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) bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -201,14 +201,9 @@ std::string AiFollow::getFollowedActor()
return mTargetActorRefId; return mTargetActorRefId;
} }
int AiFollow::getTypeId() const
{
return TypeIdFollow;
}
bool AiFollow::isCommanded() const bool AiFollow::isCommanded() const
{ {
return mCommanded; return !mOptions.mShouldCancelPreviousAi;
} }
void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
@ -222,7 +217,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
follow->mRemainingDuration = mRemainingDuration; follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId; follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow; follow->mAlwaysFollow = mAlwaysFollow;
follow->mCommanded = mCommanded; follow->mCommanded = isCommanded();
follow->mActive = mActive; follow->mActive = mActive;
ESM::AiSequence::AiPackageContainer package; ESM::AiSequence::AiPackageContainer package;

@ -53,15 +53,18 @@ namespace MWMechanics
AiFollow(const ESM::AiSequence::AiFollow* follow); 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; 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 /// Returns the actor being followed
std::string getFollowedActor(); std::string getFollowedActor();
@ -86,16 +89,15 @@ namespace MWMechanics
private: private:
/// This will make the actor always follow. /// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow; const bool mAlwaysFollow;
bool mCommanded; const float mDuration; // Hours
float mDuration; // Hours
float mRemainingDuration; // Hours float mRemainingDuration; // Hours
float mX; const float mX;
float mY; const float mY;
float mZ; const float mZ;
std::string mCellId; const std::string mCellId;
bool mActive; // have we spotted the target? bool mActive; // have we spotted the target?
int mFollowIndex; const int mFollowIndex;
static int mFollowIndexCounter; static int mFollowIndexCounter;
}; };

@ -24,7 +24,9 @@
#include <osg/Quat> #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 mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
mTargetActorRefId(""), mTargetActorRefId(""),
mTargetActorId(-1), mTargetActorId(-1),
@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
return MWWorld::Ptr(); 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() void MWMechanics::AiPackage::reset()
{ {
// reset all members // reset all members

@ -55,11 +55,39 @@ namespace MWMechanics
TypeIdCast = 11 TypeIdCast = 11
}; };
///Default constructor struct Options
AiPackage(); {
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; virtual ~AiPackage() = default;
static constexpr Options makeDefaultOptions()
{
return Options{};
}
///Clones the package ///Clones the package
virtual std::unique_ptr<AiPackage> clone() const = 0; virtual std::unique_ptr<AiPackage> clone() const = 0;
@ -69,13 +97,13 @@ namespace MWMechanics
/// Returns the TypeID of the AiPackage /// Returns the TypeID of the AiPackage
/// \see enum TypeId /// \see enum TypeId
virtual int getTypeId() const = 0; TypeId getTypeId() const { return mTypeId; }
/// Higher number is higher priority (0 being the lowest) /// 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 /// 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 {} 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); }; 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) /// 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) /// 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) /// 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)? /// 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. /// 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); } virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
// Return true if any loaded actor with this AI package must be active. /// Return true if any loaded actor with this AI package must be active.
virtual bool alwaysActive() const { return false; } bool alwaysActive() const { return mOptions.mAlwaysActive; }
/// Reset pathfinding state /// Reset pathfinding state
void reset(); void reset();
@ -139,6 +167,9 @@ namespace MWMechanics
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; 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 // TODO: all this does not belong here, move into temporary storage
PathFinder mPathFinder; PathFinder mPathFinder;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;

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

@ -27,14 +27,20 @@ namespace MWMechanics
AiPursue(const ESM::AiSequence::AiPursue* pursue); AiPursue(const ESM::AiSequence::AiPursue* pursue);
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; 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; MWWorld::Ptr getTarget() const final;
void writeState (ESM::AiSequence::AiSequence& sequence) const final; void writeState (ESM::AiSequence::AiSequence& sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
}; };
} }
#endif #endif

@ -338,7 +338,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
dest = actor.getRefData().getPosition().asVec3(); 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); stack(travelPackage, actor, false);
} }
@ -478,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
} }
case ESM::AiSequence::Ai_Travel: 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; break;
} }
case ESM::AiSequence::Ai_Escort: case ESM::AiSequence::Ai_Escort:

@ -27,14 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
namespace MWMechanics namespace MWMechanics
{ {
AiTravel::AiTravel(float x, float y, float z, bool hidden) AiTravel::AiTravel(float x, float y, float z, AiTravel*)
: mX(x),mY(y),mZ(z),mHidden(hidden) : 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) 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) bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -78,11 +90,6 @@ namespace MWMechanics
return false; return false;
} }
int AiTravel::getTypeId() const
{
return mHidden ? TypeIdInternalTravel : TypeIdTravel;
}
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
{ {
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3()))
@ -107,5 +114,20 @@ namespace MWMechanics
package.mPackage = travel.release(); package.mPackage = travel.release();
sequence.mPackages.push_back(package); 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 namespace MWMechanics
{ {
struct AiInternalTravel;
/// \brief Causes the AI to travel to the specified point /// \brief Causes the AI to travel to the specified point
class AiTravel final : public TypedAiPackage<AiTravel> class AiTravel : public TypedAiPackage<AiTravel>
{ {
public: public:
/// Default constructor AiTravel(float x, float y, float z, AiTravel* derived);
AiTravel(float x, float y, float z, bool hidden = false);
AiTravel(float x, float y, float z, AiInternalTravel* derived);
AiTravel(float x, float y, float z);
AiTravel(const ESM::AiSequence::AiTravel* travel); AiTravel(const ESM::AiSequence::AiTravel* travel);
/// Simulates the passing of time /// Simulates the passing of time
@ -28,20 +34,35 @@ namespace MWMechanics
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdTravel; }
bool useVariableSpeed() const final { return true; }
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); } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private: private:
float mX; const float mX;
float mY; const float mY;
float mZ; 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 "aiwander.hpp"
#include <algorithm>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/aisequence.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. // 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 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] = const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] =
{ {
std::string("idle2"), std::string("idle2"),
@ -94,25 +98,29 @@ namespace MWMechanics
{ {
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
} }
std::vector<unsigned char> getInitialIdle(const std::vector<unsigned char>& idle)
{
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;
}
std::vector<unsigned char> getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE])
{
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
}
} }
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat): 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), TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), 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) mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false)
{ {
mIdle.resize(8, 0);
init();
}
void AiWander::init()
{
// NOTE: mDistance and mDuration must be set already
if(mDistance < 0)
mDistance = 0;
if(mDuration < 0)
mDuration = 0;
} }
/* /*
@ -235,7 +243,6 @@ namespace MWMechanics
stopWalking(actor); stopWalking(actor);
// Reset package so it can be used again // Reset package so it can be used again
mRemainingDuration=mDuration; mRemainingDuration=mDuration;
init();
return true; return true;
} }
@ -303,11 +310,6 @@ namespace MWMechanics
return false; // AiWander package not yet completed return false; // AiWander package not yet completed
} }
bool AiWander::getRepeat() const
{
return mRepeat;
}
osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const
{ {
if (mHasDestination) if (mHasDestination)
@ -593,11 +595,6 @@ namespace MWMechanics
} }
} }
int AiWander::getTypeId() const
{
return TypeIdWander;
}
void AiWander::stopWalking(const MWWorld::Ptr& actor) void AiWander::stopWalking(const MWWorld::Ptr& actor)
{ {
mPathFinder.clearPath(); mPathFinder.clearPath();
@ -867,7 +864,7 @@ namespace MWMechanics
assert (mIdle.size() == 8); assert (mIdle.size() == 8);
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
wander->mData.mIdle[i] = mIdle[i]; wander->mData.mIdle[i] = mIdle[i];
wander->mData.mShouldRepeat = mRepeat; wander->mData.mShouldRepeat = mOptions.mRepeat;
wander->mStoredInitialActorPosition = mStoredInitialActorPosition; wander->mStoredInitialActorPosition = mStoredInitialActorPosition;
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
wander->mInitialActorPosition = mInitialActorPosition; wander->mInitialActorPosition = mInitialActorPosition;
@ -879,11 +876,12 @@ namespace MWMechanics
} }
AiWander::AiWander (const ESM::AiSequence::AiWander* wander) AiWander::AiWander (const ESM::AiSequence::AiWander* wander)
: mDistance(wander->mData.mDistance) : TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0))
, mDuration(wander->mData.mDuration) , mDistance(std::max(static_cast<short>(0), wander->mData.mDistance))
, mDuration(std::max(static_cast<short>(0), wander->mData.mDuration))
, mRemainingDuration(wander->mDurationData.mRemainingDuration) , mRemainingDuration(wander->mDurationData.mRemainingDuration)
, mTimeOfDay(wander->mData.mTimeOfDay) , mTimeOfDay(wander->mData.mTimeOfDay)
, mRepeat(wander->mData.mShouldRepeat != 0) , mIdle(getInitialIdle(wander->mData.mIdle))
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
, mHasDestination(false) , mHasDestination(false)
, mDestination(osg::Vec3f(0, 0, 0)) , mDestination(osg::Vec3f(0, 0, 0))
@ -891,11 +889,7 @@ namespace MWMechanics
{ {
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
mInitialActorPosition = wander->mInitialActorPosition; mInitialActorPosition = wander->mInitialActorPosition;
for (int i=0; i<8; ++i)
mIdle.push_back(wander->mData.mIdle[i]);
if (mRemainingDuration <= 0 || mRemainingDuration >= 24) if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
mRemainingDuration = mDuration; mRemainingDuration = mDuration;
init();
} }
} }

@ -93,16 +93,20 @@ namespace MWMechanics
bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; 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 writeState(ESM::AiSequence::AiSequence &sequence) const final;
void fastForward(const MWWorld::Ptr& actor, AiState& state) 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 MWWorld::Ptr& actor) const final;
osg::Vec3f getDestination() const final osg::Vec3f getDestination() const final
@ -114,8 +118,6 @@ namespace MWMechanics
} }
private: private:
// NOTE: mDistance and mDuration must be set already
void init();
void stopWalking(const MWWorld::Ptr& actor); void stopWalking(const MWWorld::Ptr& actor);
/// Have the given actor play an idle animation /// Have the given actor play an idle animation
@ -136,12 +138,11 @@ namespace MWMechanics
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
int mDistance; // how far the actor can wander from the spawn point const int mDistance; // how far the actor can wander from the spawn point
int mDuration; const int mDuration;
float mRemainingDuration; float mRemainingDuration;
int mTimeOfDay; const int mTimeOfDay;
std::vector<unsigned char> mIdle; const std::vector<unsigned char> mIdle;
bool mRepeat;
bool mStoredInitialActorPosition; bool mStoredInitialActorPosition;
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell
@ -176,7 +177,7 @@ namespace MWMechanics
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
static int OffsetToPreventOvercrowding(); static int OffsetToPreventOvercrowding();
}; };
} }
#endif #endif

@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) 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 = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) 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); 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 (healthLost > (acrobaticsSkill * fatigueTerm))
{ {
if (!godmode) if (!godmode)

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

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

@ -138,7 +138,7 @@ namespace MWMechanics
void setAttribute(int index, const AttributeValue &value); void setAttribute(int index, const AttributeValue &value);
// Shortcut to set only the base // Shortcut to set only the base
void setAttribute(int index, int base); void setAttribute(int index, float base);
void setHealth(const DynamicStat<float> &value); 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, // 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... // otherwise one would get different prices when exiting and re-entering the dialogue window...
int clampedDisposition = getDerivedDisposition(ptr); 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 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 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 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 f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); 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 fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0; float bootWeight = 0;
if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{ {
@ -1645,10 +1645,10 @@ namespace MWMechanics
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); 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; 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) 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; return;
base += 1; base += 1;
@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWindowManager()->playSound("skillraise"); MWBase::Environment::get().getWindowManager()->playSound("skillraise");
std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""); 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) if (readBook)
message = "#{sBookSkillMessage}\n" + message; message = "#{sBookSkillMessage}\n" + message;
@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp()
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0; 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 // "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, // 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() void MWMechanics::NpcStats::updateHealth()
{ {
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase(); const float strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(floor(0.5f * (strength + endurance))); setHealth(floor(0.5f * (strength + endurance)));
} }

@ -22,8 +22,8 @@ namespace MWMechanics
float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)
{ {
NpcStats& stats = ptr.getClass().getNpcStats(ptr); NpcStats& stats = ptr.getClass().getNpcStats(ptr);
float agility = static_cast<float>(stats.getAttribute(ESM::Attribute::Agility).getModified()); float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified()); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); 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); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
float fatigueTerm = stats.getFatigueTerm(); float fatigueTerm = stats.getFatigueTerm();
int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fRepairAmountMult")->mValue.getFloat(); .find("fRepairAmountMult")->mValue.getFloat();

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

@ -40,8 +40,8 @@ namespace MWMechanics
float resistance = getEffectResistanceAttribute(effectId, magicEffects); float resistance = getEffectResistanceAttribute(effectId, magicEffects);
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified()); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa // 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); CreatureStats& stats = actor.getClass().getCreatureStats(actor);
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);

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

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

@ -8,6 +8,16 @@ namespace MWMechanics
template <class T> template <class T>
struct TypedAiPackage : public AiPackage 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 virtual std::unique_ptr<AiPackage> clone() const override
{ {
return std::make_unique<T>(*static_cast<const T*>(this)); 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 "navmesh.hpp"
#include "actorspaths.hpp" #include "actorspaths.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "fogmanager.hpp"
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
namespace MWRender namespace MWRender
{ {
@ -204,19 +195,9 @@ namespace MWRender
, mWorkQueue(workQueue) , mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue) , mUnrefQueue(new SceneUtil::UnrefQueue)
, mNavigator(navigator) , 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) , mNightEyeFactor(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
, mFieldOfViewOverridden(false) , mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f) , mFieldOfViewOverride(0.f)
, mBorders(false)
{ {
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
@ -284,16 +265,6 @@ namespace MWRender
mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); 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 normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
const std::string heightMapPattern = Settings::Manager::getString("normal height 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"); 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); 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"); const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "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)); defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); 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->setCamera(mViewer->getCamera());
mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); mSky->setRainIntensityUniform(mWater->getRainIntensityUniform());
@ -558,9 +530,9 @@ namespace MWRender
bool RenderingManager::toggleBorders() bool RenderingManager::toggleBorders()
{ {
mBorders = !mBorders; bool borders = !mTerrain->getBordersVisible();
mTerrain->setBordersVisible(mBorders); mTerrain->setBordersVisible(borders);
return mBorders; return borders;
} }
bool RenderingManager::toggleRenderMode(RenderMode mode) bool RenderingManager::toggleRenderMode(RenderMode mode)
@ -606,46 +578,12 @@ namespace MWRender
void RenderingManager::configureFog(const ESM::Cell *cell) void RenderingManager::configureFog(const ESM::Cell *cell)
{ {
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); mFog->configure(mViewDistance, cell);
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);
} }
void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{ {
if(mDistantFog) mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
{
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;
} }
SkyManager* RenderingManager::getSkyManager() SkyManager* RenderingManager::getSkyManager()
@ -674,19 +612,11 @@ namespace MWRender
osg::Vec3f focal, cameraPos; osg::Vec3f focal, cameraPos;
mCamera->getPosition(focal, cameraPos); mCamera->getPosition(focal, cameraPos);
mCurrentCameraPos = 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); bool isUnderwater = mWater->isUnderwater(cameraPos);
mStateUpdater->setFogEnd(mLandFogEnd); mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
} mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
setFogColor(mFog->getFogColor(isUnderwater));
} }
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
@ -1335,7 +1265,7 @@ namespace MWRender
else if (it->first == "Camera" && it->second == "viewing distance") else if (it->first == "Camera" && it->second == "viewing distance")
{ {
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
if(!mDistantFog) if(!Settings::Manager::getBool("use distant fog", "Fog"))
mStateUpdater->setFogEnd(mViewDistance); mStateUpdater->setFogEnd(mViewDistance);
updateProjectionMatrix(); updateProjectionMatrix();
} }

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

@ -95,7 +95,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = Interpreter::Type_Float value =
ptr.getClass() ptr.getClass()
.getCreatureStats (ptr) .getCreatureStats (ptr)
.getAttribute(mIndex) .getAttribute(mIndex)
@ -118,7 +118,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex);
@ -140,7 +140,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass() MWMechanics::AttributeValue attribute = ptr.getClass()
@ -155,9 +155,9 @@ namespace MWScript
return; return;
if (value < 0) if (value < 0)
attribute.setBase(std::max(0, attribute.getBase() + value)); attribute.setBase(std::max(0.f, attribute.getBase() + value));
else 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); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute);
} }
@ -345,7 +345,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); 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); runtime.push (value);
} }
@ -364,7 +364,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr);
@ -386,7 +386,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::SkillValue &skill = ptr.getClass() MWMechanics::SkillValue &skill = ptr.getClass()
@ -396,14 +396,14 @@ namespace MWScript
if (value == 0) if (value == 0)
return; return;
if (((skill.getBase() <= 0) && (value < 0)) if (((skill.getBase() <= 0.f) && (value < 0.f))
|| ((skill.getBase() >= 100) && (value > 0))) || ((skill.getBase() >= 100.f) && (value > 0.f)))
return; return;
if (value < 0) if (value < 0)
skill.setBase(std::max(0, skill.getBase() + value)); skill.setBase(std::max(0.f, skill.getBase() + value));
else 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.mPlayerClassId = classId;
profile.mPlayerCell = world.getCellName(); profile.mPlayerCell = world.getCellName();
profile.mInGameTime = world.getEpochTimeStamp();
profile.mInGameTime.mGameHour = world.getTimeStamp().getHour();
profile.mInGameTime.mDay = world.getDay();
profile.mInGameTime.mMonth = world.getMonth();
profile.mInGameTime.mYear = world.getYear();
profile.mTimePlayed = mTimePlayed; profile.mTimePlayed = mTimePlayed;
profile.mDescription = description; profile.mDescription = description;

@ -421,7 +421,7 @@ namespace MWWorld
return canSwim(ptr) || canWalk(ptr) || canFly(ptr); 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"); throw std::runtime_error("class does not support skills");
} }

@ -321,7 +321,7 @@ namespace MWWorld
bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
bool isMobile(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) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const; 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 <stdexcept>
#include <components/misc/stringops.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/misc/stringops.hpp>
#include "esmstore.hpp" #include "esmstore.hpp"

@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
// rate weapon // rate weapon
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i) for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
{ {
int max = 0; float max = 0;
int maxWeaponSkill = -1; int maxWeaponSkill = -1;
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j) 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]) if (skillValue > max && !weaponSkillVisited[j])
{ {
max = skillValue; 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 fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->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); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) 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();) 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(); osg::Quat orient = it->mNode->getAttitude();
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fTargetSpellMaxSpeed")->mValue.getFloat(); .find("fTargetSpellMaxSpeed")->mValue.getFloat();
@ -405,8 +417,6 @@ namespace MWWorld
update(*it, duration); 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. // 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; std::vector<MWWorld::Ptr> targetActors;
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())

@ -60,6 +60,7 @@
#include "../mwphysics/object.hpp" #include "../mwphysics/object.hpp"
#include "../mwphysics/constants.hpp" #include "../mwphysics/constants.hpp"
#include "datetimemanager.hpp"
#include "player.hpp" #include "player.hpp"
#include "manualref.hpp" #include "manualref.hpp"
#include "cellstore.hpp" #include "cellstore.hpp"
@ -121,33 +122,11 @@ namespace MWWorld
LoadersContainer mLoaders; 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() void World::adjustSky()
{ {
if (mSky && (isCellExterior() || isCellQuasiExterior())) if (mSky && (isCellExterior() || isCellQuasiExterior()))
{ {
mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); updateSkyDate();
mRendering->setSkyEnabled(true); mRendering->setSkyEnabled(true);
} }
else else
@ -193,6 +172,8 @@ namespace MWWorld
if (mEsm[0].getFormat() == 0) if (mEsm[0].getFormat() == 0)
ensureNeededRecords(); ensureNeededRecords();
mCurrentDate.reset(new DateTimeManager());
fillGlobalVariables(); fillGlobalVariables();
mStore.setUp(true); mStore.setUp(true);
@ -227,13 +208,7 @@ namespace MWWorld
void World::fillGlobalVariables() void World::fillGlobalVariables()
{ {
mGlobalVariables.fill (mStore); mGlobalVariables.fill (mStore);
mCurrentDate->setup(mGlobalVariables);
mGameHour = &mGlobalVariables["gamehour"];
mDaysPassed = &mGlobalVariables["dayspassed"];
mDay = &mGlobalVariables["day"];
mMonth = &mGlobalVariables["month"];
mYear = &mGlobalVariables["year"];
mTimeScale = &mGlobalVariables["timescale"];
} }
void World::startNewGame (bool bypass) void World::startNewGame (bool bypass)
@ -310,6 +285,7 @@ namespace MWWorld
mPhysics->toggleCollisionMode(); mPhysics->toggleCollisionMode();
MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer();
mCurrentDate->setup(mGlobalVariables);
} }
void World::clear() void World::clear()
@ -639,26 +615,20 @@ namespace MWWorld
void World::setGlobalInt (const std::string& name, int value) void World::setGlobalInt (const std::string& name, int value)
{ {
if (name=="gamehour") bool dateUpdated = mCurrentDate->updateGlobalInt(name, value);
setHour (value); if (dateUpdated)
else if (name=="day") updateSkyDate();
setDay (value);
else if (name=="month") mGlobalVariables[name].setInteger (value);
setMonth (value);
else
mGlobalVariables[name].setInteger (value);
} }
void World::setGlobalFloat (const std::string& name, float value) void World::setGlobalFloat (const std::string& name, float value)
{ {
if (name=="gamehour") bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value);
setHour (value); if (dateUpdated)
else if (name=="day") updateSkyDate();
setDay(static_cast<int>(value));
else if (name=="month") mGlobalVariables[name].setFloat(value);
setMonth(static_cast<int>(value));
else
mGlobalVariables[name].setFloat (value);
} }
int World::getGlobalInt (const std::string& name) const int World::getGlobalInt (const std::string& name) const
@ -676,6 +646,11 @@ namespace MWWorld
return mGlobalVariables.getType (name); return mGlobalVariables.getType (name);
} }
std::string World::getMonthName (int month) const
{
return mCurrentDate->getMonthName(month);
}
std::string World::getCellName (const MWWorld::CellStore *cell) const std::string World::getCellName (const MWWorld::CellStore *cell) const
{ {
if (!cell) if (!cell)
@ -894,130 +869,29 @@ namespace MWWorld
} }
mWeatherManager->advanceTime (hours, incremental); mWeatherManager->advanceTime (hours, incremental);
mCurrentDate->advanceTime(hours, mGlobalVariables);
updateSkyDate();
if (!incremental) if (!incremental)
{ {
mRendering->notifyWorldSpaceChanged(); mRendering->notifyWorldSpaceChanged();
mProjectileManager->clear(); 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 float World::getTimeScaleFactor() const
{
return mDay->getInteger();
}
int World::getMonth() const
{
return mMonth->getInteger();
}
int World::getYear() const
{ {
return mYear->getInteger(); return mCurrentDate->getTimeScaleFactor();
} }
std::string World::getMonthName (int month) const TimeStamp World::getTimeStamp() const
{ {
if (month==-1) return mCurrentDate->getTimeStamp();
month = getMonth();
const int months = 12;
if (month<0 || month>=months)
return "";
static const char *monthNames[months] =
{
"sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand",
"sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed",
"sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar"
};
return mStore.get<ESM::GameSetting>().find (monthNames[month])->mValue.getString();
} }
TimeStamp World::getTimeStamp() const ESM::EpochTimeStamp World::getEpochTimeStamp() const
{ {
return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); return mCurrentDate->getEpochTimeStamp();
} }
bool World::toggleSky() bool World::toggleSky()
@ -1042,11 +916,6 @@ namespace MWWorld
mRendering->skySetMoonColour (red); 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) void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
{ {
mPhysics->clearQueuedMovement(); mPhysics->clearQueuedMovement();
@ -1089,6 +958,8 @@ namespace MWWorld
changeToExteriorCell (position, adjustPlayerPos, changeEvent); changeToExteriorCell (position, adjustPlayerPos, changeEvent);
else else
changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent); changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent);
mCurrentDate->setup(mGlobalVariables);
} }
void World::markCellAsUnchanged() void World::markCellAsUnchanged()
@ -3968,4 +3839,10 @@ namespace MWWorld
mNavigator->reportStats(frameNumber, stats); mNavigator->reportStats(frameNumber, stats);
mPhysics->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 namespace MWWorld
{ {
class DateTimeManager;
class WeatherManager; class WeatherManager;
class Player; class Player;
class ProjectileManager; class ProjectileManager;
@ -85,13 +86,6 @@ namespace MWWorld
LocalScripts mLocalScripts; LocalScripts mLocalScripts;
MWWorld::Globals mGlobalVariables; MWWorld::Globals mGlobalVariables;
ESM::Variant* mGameHour;
ESM::Variant* mDaysPassed;
ESM::Variant* mDay;
ESM::Variant* mMonth;
ESM::Variant* mYear;
ESM::Variant* mTimeScale;
Cells mCells; Cells mCells;
std::string mCurrentWorldSpace; std::string mCurrentWorldSpace;
@ -102,6 +96,7 @@ namespace MWWorld
std::unique_ptr<MWRender::RenderingManager> mRendering; std::unique_ptr<MWRender::RenderingManager> mRendering;
std::unique_ptr<MWWorld::Scene> mWorldScene; std::unique_ptr<MWWorld::Scene> mWorldScene;
std::unique_ptr<MWWorld::WeatherManager> mWeatherManager; std::unique_ptr<MWWorld::WeatherManager> mWeatherManager;
std::unique_ptr<MWWorld::DateTimeManager> mCurrentDate;
std::shared_ptr<ProjectileManager> mProjectileManager; std::shared_ptr<ProjectileManager> mProjectileManager;
bool mSky; bool mSky;
@ -139,7 +134,6 @@ namespace MWWorld
World& operator= (const World&); World& operator= (const World&);
void updateWeather(float duration, bool paused = false); void updateWeather(float duration, bool paused = false);
int getDaysPerMonth (int month) const;
void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags);
@ -173,6 +167,8 @@ namespace MWWorld
void fillGlobalVariables(); void fillGlobalVariables();
void updateSkyDate();
/** /**
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
* @param fileCollections- Container which holds content file names and their paths * @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; void advanceTime (double hours, bool incremental = false) override;
///< Advance in-game time. ///< 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; std::string getMonthName (int month = -1) const override;
///< Return name of month (-1: current month) ///< Return name of month (-1: current month)
TimeStamp getTimeStamp() const override; 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; bool toggleSky() override;
///< \return Resulting mode ///< \return Resulting mode

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

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

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

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

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

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

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

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

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

@ -14,6 +14,14 @@ struct TimeStamp
int mDay; int mDay;
}; };
struct EpochTimeStamp
{
float mGameHour;
int mDay;
int mMonth;
int mYear;
};
// Pixel color value. Standard four-byte rr,gg,bb,aa format. // Pixel color value. Standard four-byte rr,gg,bb,aa format.
typedef uint32_t Color; typedef uint32_t Color;
@ -49,6 +57,26 @@ struct Position
}; };
#pragma pack(pop) #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> template <int a, int b, int c, int d>
struct FourCC struct FourCC
{ {

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

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

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

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

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

@ -44,12 +44,13 @@ void ESM::Player::load (ESMReader &esm)
checkPrevItems = false; checkPrevItems = false;
} }
bool intFallback = esm.getFormat() < 11;
if (esm.hasMoreSubs()) if (esm.hasMoreSubs())
{ {
for (int i=0; i<ESM::Attribute::Length; ++i) 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) 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 mCurrentCrimeId;
int mPaidCrimeId; int mPaidCrimeId;
StatState<int> mSaveAttributes[ESM::Attribute::Length]; StatState<float> mSaveAttributes[ESM::Attribute::Length];
StatState<int> mSaveSkills[ESM::Skill::Length]; StatState<float> mSaveSkills[ESM::Skill::Length];
typedef std::map<std::string, std::string> PreviousItems; // previous equipped items, needed for bound spells typedef std::map<std::string, std::string> PreviousItems; // previous equipped items, needed for bound spells
PreviousItems mPreviousItems; PreviousItems mPreviousItems;

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

@ -4,6 +4,8 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "defs.hpp"
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -17,14 +19,6 @@ namespace ESM
static int sCurrentFormat; static int sCurrentFormat;
struct TimeStamp
{
float mGameHour;
int mDay;
int mMonth;
int mYear;
};
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
std::string mPlayerName; std::string mPlayerName;
int mPlayerLevel; int mPlayerLevel;
@ -36,7 +30,7 @@ namespace ESM
std::string mPlayerClassName; std::string mPlayerClassName;
std::string mPlayerCell; std::string mPlayerCell;
TimeStamp mInGameTime; EpochTimeStamp mInGameTime;
double mTimePlayed; double mTimePlayed;
std::string mDescription; std::string mDescription;
std::vector<char> mScreenshot; // raw jpg-encoded data 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) {} StatState<T>::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {}
template<typename T> template<typename T>
void StatState<T>::load(ESMReader &esm) void StatState<T>::load(ESMReader &esm, bool intFallback)
{ {
esm.getHNT(mBase, "STBA"); // 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);
mMod = 0; int mod = 0;
esm.getHNOT(mMod, "STMO"); esm.getHNOT(mod, "STMO");
mCurrent = 0; mMod = static_cast<float>(mod);
esm.getHNOT(mCurrent, "STCU");
// mDamage was changed to a float; ensure backwards compatibility int current = 0;
T oldDamage = 0; esm.getHNOT(current, "STCU");
esm.getHNOT(oldDamage, "STDA"); mCurrent = static_cast<float>(current);
mDamage = static_cast<float>(oldDamage);
// 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 = 0;
esm.getHNOT(mDamage, "STDF");
mProgress = 0;
}
esm.getHNOT(mDamage, "STDF"); esm.getHNOT(mDamage, "STDF");

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

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

Loading…
Cancel
Save