Merge branch 'master' of https://github.com/zinnschlag/openmw.git into AIFix2

Conflicts:
	apps/openmw/mwmechanics/aifollow.cpp
actorid
gus 11 years ago
commit 707e579dfe

@ -93,8 +93,6 @@ set(OENGINE_GUI
)
set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp
${LIBDIR}/openengine/bullet/btKinematicCharacterController.h
${LIBDIR}/openengine/bullet/BtOgre.cpp
${LIBDIR}/openengine/bullet/BtOgreExtras.h
${LIBDIR}/openengine/bullet/BtOgreGP.h
@ -188,8 +186,6 @@ if (WIN32)
add_definitions(-DSDL_MAIN_HANDLED)
else (WIN32)
set(PLATFORM_INCLUDE_DIR "")
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
include_directories(${UUID_INCLUDE_DIR})
endif (WIN32)
if (MSVC10)
set(PLATFORM_INCLUDE_DIR "")
@ -241,7 +237,6 @@ include_directories("."
${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR}
${UUID_INCLUDE_DIR}
${LIBDIR}
)
@ -411,46 +406,6 @@ IF(NOT WIN32 AND NOT APPLE)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
IF (DPKG_PROGRAM)
## Debian Specific
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION )
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}")
EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME )
EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
ELSE()
SET(VERSION_STRING "${OPENMW_VERSION}")
SET(PACKAGE_MAINTAINER "unknown")
ENDIF()
SET(CPACK_GENERATOR "DEB")
SET(CPACK_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org")
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}")
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind.
Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
EXECUTE_PROCESS(
COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
INCLUDE(CPack)
ENDIF(DPKG_PROGRAM)
ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32)

@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Biped) properties += "Biped ";
if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon ";
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
ESM::Creature::Walks|
ESM::Creature::Swims|
ESM::Creature::Flies|
ESM::Creature::Biped|
ESM::Creature::Bipedal|
ESM::Creature::Respawn|
ESM::Creature::Weapon|
ESM::Creature::Skeleton|
@ -717,16 +717,26 @@ std::string landFlags(int flags)
return properties;
}
std::string leveledListFlags(int flags)
std::string itemListFlags(int flags)
{
std::string properties = "";
if (flags == 0) properties += "[None] ";
if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels ";
// This flag apparently not present on creature lists...
if (flags & ESM::LeveledListBase::Each) properties += "Each ";
if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels ";
if (flags & ESM::ItemLevList::Each) properties += "Each ";
int unused = (0xFFFFFFFF ^
(ESM::LeveledListBase::AllLevels|
ESM::LeveledListBase::Each));
(ESM::ItemLevList::AllLevels|
ESM::ItemLevList::Each));
if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
return properties;
}
std::string creatureListFlags(int flags)
{
std::string properties = "";
if (flags == 0) properties += "[None] ";
if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels ";
int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels);
if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
return properties;
@ -764,34 +774,19 @@ std::string magicEffectFlags(int flags)
{
std::string properties = "";
if (flags == 0) properties += "[None] ";
// Enchanting & SpellMaking occur on the same list of effects.
// "EXTRA SPELL" appears in the construction set under both the
// spell making and enchanting tabs as an allowed effect. Since
// most of the effects without this flags are defective in various
// ways, it's still very unclear what these flag bits are.
if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking ";
if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting ";
if (flags & 0x00000040) properties += "RangeNoSelf ";
if (flags & 0x00000080) properties += "RangeTouch ";
if (flags & 0x00000100) properties += "RangeTarget ";
if (flags & 0x00001000) properties += "Unknown2 ";
if (flags & 0x00000001) properties += "AffectSkill ";
if (flags & 0x00000002) properties += "AffectAttribute ";
if (flags & ESM::MagicEffect::TargetAttribute) properties += "TargetAttribute ";
if (flags & ESM::MagicEffect::TargetSkill) properties += "TargetSkill ";
if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration ";
if (flags & 0x00000008) properties += "NoMagnitude ";
if (flags & 0x00000010) properties += "Negative ";
if (flags & 0x00000020) properties += "Unknown1 ";
// ESM componet says 0x800 is negative, but none of the magic
// effects have this flags set.
if (flags & ESM::MagicEffect::Negative) properties += "Unused ";
// Since only Chameleon has this flag it could be anything
// that uniquely distinguishes Chameleon.
if (flags & 0x00002000) properties += "Chameleon ";
if (flags & 0x00004000) properties += "Bound ";
if (flags & 0x00008000) properties += "Summon ";
// Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded
if (flags & 0x00010000) properties += "Unknown3 ";
if (flags & 0x00020000) properties += "Absorb ";
if (flags & ESM::MagicEffect::NoMagnitude) properties += "NoMagnitude ";
if (flags & ESM::MagicEffect::Harmful) properties += "Harmful ";
if (flags & ESM::MagicEffect::ContinuousVfx) properties += "ContinuousVFX ";
if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf ";
if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch ";
if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget ";
if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage ";
if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable ";
if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable ";
if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked ";
if (flags & 0xFFFC0000) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
return properties;

@ -50,7 +50,8 @@ std::string cellFlags(int flags);
std::string containerFlags(int flags);
std::string creatureFlags(int flags);
std::string landFlags(int flags);
std::string leveledListFlags(int flags);
std::string creatureListFlags(int flags);
std::string itemListFlags(int flags);
std::string lightFlags(int flags);
std::string magicEffectFlags(int flags);
std::string npcFlags(int flags);

@ -13,8 +13,8 @@ void printAIPackage(ESM::AIPackage p)
std::cout << " Distance: " << p.mWander.mDistance << std::endl;
std::cout << " Duration: " << p.mWander.mDuration << std::endl;
std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl;
if (p.mWander.mUnk != 1)
std::cout << " Unknown: " << (int)p.mWander.mUnk << std::endl;
if (p.mWander.mShouldRepeat != 1)
std::cout << " Should repeat: " << (bool)p.mWander.mShouldRepeat << std::endl;
std::cout << " Idle: ";
for (int i = 0; i != 8; i++)
@ -834,7 +834,7 @@ template<>
void Record<ESM::CreatureLevList>::print()
{
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
@ -846,11 +846,11 @@ template<>
void Record<ESM::ItemLevList>::print()
{
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
std::cout << " Inventory: Count: " << iit->mLevel
std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl;
}
@ -958,7 +958,7 @@ void Record<ESM::MagicEffect>::print()
std::cout << " RGB Color: " << "("
<< mData.mData.mRed << ","
<< mData.mData.mGreen << ","
<< mData.mData.mGreen << ")" << std::endl;
<< mData.mData.mBlue << ")" << std::endl;
}
template<>

@ -235,7 +235,7 @@ namespace
{
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
{
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return dir->path();
}
}
@ -243,7 +243,7 @@ namespace
{
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
{
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return dir->path();
}
}
@ -255,7 +255,7 @@ namespace
{
for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
{
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return true;
}

@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish",
"Moons:Script Color",
// blood
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
0
};

@ -38,7 +38,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck
birthsigncheck spellcheck referenceablecheck
)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,78 @@
#ifndef REFERENCEABLECHECKSTAGE_H
#define REFERENCEABLECHECKSTAGE_H
#include "../world/universalid.hpp"
#include "../doc/stage.hpp"
#include "../world/data.hpp"
#include "../world/refiddata.hpp"
namespace CSMTools
{
class ReferenceableCheckStage : public CSMDoc::Stage
{
public:
ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable,
const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions);
virtual void perform(int stage, std::vector< std::string >& messages);
virtual int setup();
private:
//CONCRETE CHECKS
void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages);
void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages);
void potionCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Potion>& records, std::vector<std::string>& messages);
void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Apparatus>& records, std::vector<std::string>& messages);
void armorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Armor>& records, std::vector<std::string>& messages);
void clothingCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Clothing>& records, std::vector<std::string>& messages);
void containerCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Container>& records, std::vector<std::string>& messages);
void creatureCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Creature>& records, std::vector<std::string>& messages);
void doorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Door>& records, std::vector<std::string>& messages);
void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Ingredient>& records, std::vector<std::string>& messages);
void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::CreatureLevList>& records, std::vector<std::string>& messages);
void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::ItemLevList>& records, std::vector<std::string>& messages);
void lightCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Light>& records, std::vector<std::string>& messages);
void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Lockpick>& records, std::vector<std::string>& messages);
void miscCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Miscellaneous>& records, std::vector<std::string>& messages);
void npcCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::NPC>& records, std::vector<std::string>& messages);
void weaponCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Weapon>& records, std::vector<std::string>& messages);
void probeCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Probe>& records, std::vector<std::string>& messages);
void repairCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Repair>& records, std::vector<std::string>& messages);
void staticCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Static>& records, std::vector<std::string>& messages);
//FINAL CHECK
void finalCheck(std::vector<std::string>& messages);
//TEMPLATE CHECKS
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
std::vector<std::string>& messages,
const std::string& someID,
bool enchantable); //for all enchantable items.
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
std::vector<std::string>& messages,
const std::string& someID); //for non-enchantable items.
template<typename TOOL> void toolCheck(const TOOL& someTool,
std::vector<std::string>& messages,
const std::string& someID,
bool canbebroken); //for tools with uses.
template<typename TOOL> void toolCheck(const TOOL& someTool,
std::vector<std::string>& messages,
const std::string& someID); //for tools without uses.
template<typename LIST> void listCheck(const LIST& someList,
std::vector< std::string >& messages,
const std::string& someID);
const CSMWorld::RefIdData& mReferencables;
const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mPlayerPresent;
};
}
#endif // REFERENCEABLECHECKSTAGE_H

@ -68,4 +68,4 @@ void CSMTools::ReportModel::add (const std::string& row)
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
{
return mRows.at (row).first;
}
}

@ -19,6 +19,7 @@
#include "regioncheck.hpp"
#include "birthsigncheck.hpp"
#include "spellcheck.hpp"
#include "referenceablecheck.hpp"
CSMDoc::Operation *CSMTools::Tools::get (int type)
{
@ -74,6 +75,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
}
return mVerifier;
@ -138,4 +141,5 @@ void CSMTools::Tools::verifierMessage (const QString& message, int type)
if (iter!=mActiveReports.end())
mReports[iter->second]->add (message.toStdString());
}
}

@ -220,7 +220,7 @@ int CSMWorld::Columns::getId (const std::string& name)
std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i)
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName))
if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))
return sNames[i].mId;
return -1;

@ -67,7 +67,7 @@ int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
for (; range.first!=range.second; ++range.first)
if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId)
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
return std::distance (getRecords().begin(), range.first);
return -1;
@ -177,8 +177,8 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
RecordConstIterator end = begin;
for (; end!=getRecords().end(); ++end)
if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2)
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
break;
return Range (begin, end);
}
}

@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
unsigned int mFlag;
} sCreatureFlagTable[] =
{
{ Columns::ColumnId_Biped, ESM::Creature::Biped },
{ Columns::ColumnId_Biped, ESM::Creature::Bipedal },
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
{ Columns::ColumnId_NoMovement, ESM::Creature::None },
{ Columns::ColumnId_Swims, ESM::Creature::Swims },
@ -549,3 +549,9 @@ void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{
mData.save (index, writer);
}
const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const
{
return mData;
}

@ -107,7 +107,10 @@ namespace CSMWorld
/// \return Success?
void save (int index, ESM::ESMWriter& writer) const;
const RefIdData& getDataSet() const; //I can't figure out a better name for this one :(
};
}
#endif

@ -230,4 +230,104 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
throw std::logic_error ("invalid local index type");
iter->second->save (localIndex.first, writer);
}
const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const
{
return mBooks;
}
const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const
{
return mActivators;
}
const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const
{
return mPotions;
}
const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const
{
return mApparati;
}
const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const
{
return mArmors;
}
const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const
{
return mClothing;
}
const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const
{
return mContainers;
}
const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const
{
return mCreatures;
}
const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const
{
return mDoors;
}
const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const
{
return mIngredients;
}
const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const
{
return mCreatureLevelledLists;
}
const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const
{
return mItemLevelledLists;
}
const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const
{
return mLights;
}
const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const
{
return mLockpicks;
}
const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const
{
return mMiscellaneous;
}
const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const
{
return mNpcs;
}
const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const
{
return mWeapons;
}
const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const
{
return mProbes;
}
const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const
{
return mRepairs;
}
const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const
{
return mStatics;
}

@ -219,7 +219,33 @@ namespace CSMWorld
/// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const;
//RECORD CONTAINERS ACCESS METHODS
const RefIdDataContainer<ESM::Book>& getBooks() const;
const RefIdDataContainer<ESM::Activator>& getActivators() const;
const RefIdDataContainer<ESM::Potion>& getPotions() const;
const RefIdDataContainer<ESM::Apparatus>& getApparati() const;
const RefIdDataContainer<ESM::Armor>& getArmors() const;
const RefIdDataContainer<ESM::Clothing>& getClothing() const;
const RefIdDataContainer<ESM::Container>& getContainers() const;
const RefIdDataContainer<ESM::Creature>& getCreatures() const;
const RefIdDataContainer<ESM::Door>& getDoors() const;
const RefIdDataContainer<ESM::Ingredient>& getIngredients() const;
const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const;
const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const;
const RefIdDataContainer<ESM::Light>& getLights() const;
const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const;
const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const;
const RefIdDataContainer<ESM::NPC>& getNPCs() const;
const RefIdDataContainer<ESM::Weapon >& getWeapons() const;
const RefIdDataContainer<ESM::Probe >& getProbes() const;
const RefIdDataContainer<ESM::Repair>& getRepairs() const;
const RefIdDataContainer<ESM::Static>& getStatics() const;
};
}
#endif

@ -6,6 +6,7 @@
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
#include <OgreEntity.h>
#include <OgreCamera.h>
namespace CSVRender
{

@ -20,7 +20,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst
terrainstorage renderconst effectmanager
)
add_openmw_dir (mwinput
@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
disease pickpocket
disease pickpocket levelledlist
)
add_openmw_dir (mwbase

@ -77,8 +77,12 @@ namespace MWBase
virtual void setPlayerClass (const ESM::Class& class_) = 0;
///< Set player class to custom class.
virtual void restoreDynamicStats() = 0;
///< If the player is sleeping, this should be called every hour.
virtual void rest(bool sleep) = 0;
///< If the player is sleeping or waiting, this should be called every hour.
/// @param sleep is the player sleeping or waiting?
virtual int getHoursToRest() const = 0;
///< Calculate how many hours the player needs to rest in order to be fully healed
virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0;
///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.
@ -113,6 +117,8 @@ namespace MWBase
OffenseType type, int arg=0) = 0;
/// Utility to check if taking this item is illegal and calling commitCrime if so
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0;
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;
/// Attempt sleeping in a bed. If this is illegal, call commitCrime.
/// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;

@ -227,9 +227,6 @@ namespace MWBase
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), bool showInDialogueModeOnly = false) = 0;
virtual void staticMessageBox(const std::string& message) = 0;
virtual void removeStaticMessageBox() = 0;
virtual void enterPressed () = 0;
virtual void activateKeyPressed () = 0;
virtual int readPressedButton() = 0;
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)

@ -157,6 +157,10 @@ namespace MWBase
///< Return a pointer to a liveCellRef with the given name.
/// \param activeOnly do non search inactive cells.
virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly) = 0;
///< Return a pointer to a liveCellRef with the given name.
/// \param activeOnly do non search inactive cells.
virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0;
///< Return a pointer to a liveCellRef with the given Ogre handle.
@ -451,6 +455,17 @@ namespace MWBase
/// Update the value of some globals according to the world state, which may be used by dialogue entries.
/// This should be called when initiating a dialogue.
virtual void updateDialogueGlobals() = 0;
/// Moves all stolen items from \a ptr to the closest evidence chest.
virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0;
virtual void goToJail () = 0;
/// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList) = 0;
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
};
}

@ -61,6 +61,17 @@ namespace MWClass
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
inited = true;
}
@ -165,6 +176,62 @@ namespace MWClass
void Creature::hit(const MWWorld::Ptr& ptr, int type) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
// TODO: where is the distance defined?
std::pair<MWWorld::Ptr, Ogre::Vector3> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100);
if (result.first.isEmpty())
return; // Didn't hit anything
MWWorld::Ptr victim = result.first;
if (!victim.getClass().isActor())
return; // Can't hit non-actors
Ogre::Vector3 hitPosition = result.second;
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = ref->mBase->mData.mCombat +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= otherstats.getEvasion();
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
return;
}
int min,max;
switch (type)
{
case 0:
min = ref->mBase->mData.mAttack[0];
max = ref->mBase->mData.mAttack[1];
break;
case 1:
min = ref->mBase->mData.mAttack[2];
max = ref->mBase->mData.mAttack[3];
break;
case 2:
default:
min = ref->mBase->mData.mAttack[4];
max = ref->mBase->mData.mAttack[5];
break;
}
float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0);
// TODO: do not do this if the attack is blocked
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true);
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -191,6 +258,19 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
getCreatureStats(ptr).setKnockedDown(true);
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth)
{
if(damage > 0.0f)
@ -286,10 +366,51 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
/// \todo what about the rest?
return walkSpeed;
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f;
else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
world->isLevitationEnabled())
{
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(running)
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
return moveSpeed;
}
MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const
@ -461,6 +582,49 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
const ESM::Skill* skillRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skill);
switch (skillRecord->mData.mSpecialization)
{
case ESM::Class::Combat:
return ref->mBase->mData.mCombat;
case ESM::Class::Magic:
return ref->mBase->mData.mMagic;
case ESM::Class::Stealth:
return ref->mBase->mData.mStealth;
default:
throw std::runtime_error("invalid specialisation");
}
}
int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::Creature::Metal)
return 2;
return 0;
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
const ESM::GameSetting *Creature::fSneakSpeedMultiplier;
const ESM::GameSetting *Creature::fAthleticsRunBonus;
const ESM::GameSetting *Creature::fBaseRunMultiplier;
const ESM::GameSetting *Creature::fMinFlySpeed;
const ESM::GameSetting *Creature::fMaxFlySpeed;
const ESM::GameSetting *Creature::fSwimRunBase;
const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
const ESM::GameSetting *Creature::fKnockDownMult;
const ESM::GameSetting *Creature::iKnockDownOddsMult;
const ESM::GameSetting *Creature::iKnockDownOddsBase;
}

@ -16,6 +16,18 @@ namespace MWClass
static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
public:
@ -101,6 +113,11 @@ namespace MWClass
}
virtual bool isFlying (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
};
}

@ -3,8 +3,6 @@
#include <memory>
#include <boost/algorithm/string.hpp>
#include <OgreSceneNode.h>
#include <components/esm/loadmgef.hpp>
@ -242,6 +240,9 @@ namespace MWClass
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
fWereWolfRunMult = gmst.find("fWereWolfRunMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
inited = true;
}
@ -307,6 +308,17 @@ namespace MWClass
autoCalculateSkills(ref->mBase, data->mNpcStats);
}
if (data->mNpcStats.getFactionRanks().size())
{
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt();
int rank = data->mNpcStats.getFactionRanks().begin()->second;
data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
}
data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage);
data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello);
@ -425,11 +437,27 @@ namespace MWClass
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr();
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
getCreatureStats(ptr).setFatigue(fatigue);
float dist = 100.0f * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle and where to spawn the blood effect
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first;
// TODO: Use second to work out the hit angle
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
MWWorld::Ptr victim = result.first;
Ogre::Vector3 hitPosition = result.second;
if(victim.isEmpty()) // Didn't hit anything
return;
@ -486,12 +514,6 @@ namespace MWClass
weapon.getCellRef().mCharge = weapmaxhealth;
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if(!othercls.hasDetected(victim, ptr))
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1,
@ -513,12 +535,6 @@ namespace MWClass
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
damage = stats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
if(!othercls.hasDetected(victim, ptr))
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f)
|| (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0);
@ -545,6 +561,16 @@ namespace MWClass
if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, weapskill, 0);
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(!detected)
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
if (othercls.getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
if (!enchantmentName.empty())
@ -578,6 +604,10 @@ namespace MWClass
}
}
// TODO: do not do this if the attack is blocked
if (healthdmg)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
}
@ -587,6 +617,10 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime
if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
@ -601,7 +635,7 @@ namespace MWClass
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
{
const std::string &script = ptr.get<ESM::NPC>()->mBase->mScript;
const std::string &script = ptr.getClass().getScript(ptr);
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
if(!script.empty())
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
@ -615,8 +649,27 @@ namespace MWClass
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc.
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
getCreatureStats(ptr).setAttacked(true);//used in CharacterController
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (roll < chance)
{
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
}
getCreatureStats(ptr).setAttacked(true);
// Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
getCreatureStats(ptr).setKnockedDown(true);
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(object.isEmpty())
{
@ -728,10 +781,10 @@ namespace MWClass
}
if(getCreatureStats(ptr).isDead())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(get(actor).getStance(actor, MWWorld::Class::Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
if(get(ptr).getCreatureStats(ptr).isHostile())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
}
@ -759,80 +812,6 @@ namespace MWClass
return ref->mBase->mScript;
}
void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force);
break;
case Sneak:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force);
break;
case Combat:
throw std::runtime_error ("combat stance not enforcable for NPCs");
}
}
void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_Run, set);
break;
case Sneak:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set);
break;
case Combat:
// Combat stance ignored for now; need to be determined based on draw state instead of
// being maunally set.
break;
}
}
bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun))
return true;
return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Run);
case Sneak:
if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak))
return true;
return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak);
case Combat:
return false;
}
return false;
}
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
@ -841,11 +820,14 @@ namespace MWClass
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(Npc::getStance(ptr, Sneak, false))
if(sneaking)
walkSpeed *= fSneakSpeedMultiplier->getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
@ -869,14 +851,14 @@ namespace MWClass
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(Npc::getStance(ptr, Run, false))
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false))
else if(running && !sneaking)
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
@ -908,7 +890,7 @@ namespace MWClass
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
x *= encumbranceTerm;
if(Npc::getStance(ptr, Run, false))
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
x *= fJumpRunMultiplier->getFloat();
x *= npcdata->mNpcStats.getFatigueTerm();
x -= -627.2f;/*gravity constant*/
@ -1020,7 +1002,8 @@ namespace MWClass
float Npc::getCapacity (const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
return stats.getAttribute(0).getModified()*5;
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat();
return stats.getAttribute(0).getModified()*fEncumbranceStrMult;
}
float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const
@ -1234,6 +1217,22 @@ namespace MWClass
return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
}
int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
{
return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified();
}
int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
}
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
@ -1250,4 +1249,7 @@ namespace MWClass
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
const ESM::GameSetting *Npc::fJumpRunMultiplier;
const ESM::GameSetting *Npc::fWereWolfRunMult;
const ESM::GameSetting *Npc::fKnockDownMult;
const ESM::GameSetting *Npc::iKnockDownOddsMult;
const ESM::GameSetting *Npc::iKnockDownOddsBase;
}

@ -33,6 +33,9 @@ namespace MWClass
static const ESM::GameSetting *fJumpAcroMultiplier;
static const ESM::GameSetting *fJumpRunMultiplier;
static const ESM::GameSetting *fWereWolfRunMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
public:
@ -81,16 +84,6 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr
virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const;
///< Force or unforce a stance.
virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const;
///< Set or unset a stance.
virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false)
const;
///< Check if a stance is active or not.
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed.
@ -147,6 +140,11 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual bool isActor() const {
return true;
}

@ -24,7 +24,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
// actor id
if (!info.mActor.empty())
{
if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor))
if ( !Misc::StringUtils::ciEqual(info.mActor, MWWorld::Class::get (mActor).getId (mActor)))
return false;
}
else if (isCreature)
@ -41,7 +41,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
if (Misc::StringUtils::lowerCase (info.mRace)!= Misc::StringUtils::lowerCase (cellRef->mBase->mRace))
if (!Misc::StringUtils::ciEqual(info.mRace, cellRef->mBase->mRace))
return false;
}
@ -53,7 +53,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
if ( Misc::StringUtils::lowerCase (info.mClass)!= Misc::StringUtils::lowerCase (cellRef->mBase->mClass))
if ( !Misc::StringUtils::ciEqual(info.mClass, cellRef->mBase->mClass))
return false;
}
@ -110,7 +110,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
// check cell
if (!info.mCell.empty())
if (Misc::StringUtils::lowerCase (player.getCell()->mCell->mName) != Misc::StringUtils::lowerCase (info.mCell))
if (!Misc::StringUtils::ciEqual(player.getCell()->mCell->mName, info.mCell))
return false;
return true;
@ -188,7 +188,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
int i = 0;
for (; i<static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name)
if (Misc::StringUtils::ciEqual(script->mVarNames[i], name))
break;
if (i>=static_cast<int> (script->mVarNames.size()))
@ -262,7 +262,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
std::string name = select.getName();
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
if (Misc::StringUtils::lowerCase(iter->getCellRef().mRefID) == name)
if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, name))
sum += iter->getRefData().getCount();
return sum;
@ -429,23 +429,23 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotId:
return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor));
return !Misc::StringUtils::ciEqual(MWWorld::Class::get (mActor).getId (mActor), select.getName());
case SelectWrapper::Function_NotFaction:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mFaction)!=select.getName();
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mFaction, select.getName());
case SelectWrapper::Function_NotClass:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mClass)!=select.getName();
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mClass, select.getName());
case SelectWrapper::Function_NotRace:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)!=select.getName();
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, select.getName());
case SelectWrapper::Function_NotCell:
return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName();
return !Misc::StringUtils::ciEqual(mActor.getCell()->mCell->mName, select.getName());
case SelectWrapper::Function_NotLocal:
{
@ -462,7 +462,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
int i = 0;
for (; i < static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name)
if (Misc::StringUtils::ciEqual(script->mVarNames[i], name))
break;
if (i >= static_cast<int> (script->mVarNames.size()))
@ -478,8 +478,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameRace:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)!=
Misc::StringUtils::lowerCase (player.get<ESM::NPC>()->mBase->mRace);
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, player.get<ESM::NPC>()->mBase->mRace);
case SelectWrapper::Function_SameFaction:
@ -525,7 +524,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_Detected:
return MWWorld::Class::get (mActor).hasDetected (mActor, player);
return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor);
case SelectWrapper::Function_Attacked:

@ -1,6 +1,5 @@
#include "birth.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp"
@ -77,7 +76,7 @@ namespace MWGui
size_t count = mBirthList->getItemCount();
for (size_t i = 0; i < count; ++i)
{
if (boost::iequals(*mBirthList->getItemDataAt<std::string>(i), birthId))
if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt<std::string>(i), birthId))
{
mBirthList->setIndexSelected(i);
MyGUI::Button* okButton;
@ -112,7 +111,7 @@ namespace MWGui
getWidget(okButton, "OKButton");
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
if (boost::iequals(mCurrentBirthId, *birthId))
if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId))
return;
mCurrentBirthId = *birthId;
@ -148,7 +147,7 @@ namespace MWGui
mBirthList->setIndexSelected(index);
mCurrentBirthId = it2->first;
}
else if (boost::iequals(it2->first, mCurrentBirthId))
else if (Misc::StringUtils::ciEqual(it2->first, mCurrentBirthId))
{
mBirthList->setIndexSelected(index);
}

@ -1,7 +1,5 @@
#include "class.hpp"
#include <boost/algorithm/string.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -29,11 +27,12 @@ namespace MWGui
MyGUI::Button* backButton;
getWidget(backButton, "BackButton");
backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}");
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}");
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
}
@ -127,7 +126,7 @@ namespace MWGui
size_t count = mClassList->getItemCount();
for (size_t i = 0; i < count; ++i)
{
if (boost::iequals(*mClassList->getItemDataAt<std::string>(i), classId))
if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt<std::string>(i), classId))
{
mClassList->setIndexSelected(i);
MyGUI::Button* okButton;
@ -162,7 +161,7 @@ namespace MWGui
getWidget(okButton, "OKButton");
const std::string *classId = mClassList->getItemDataAt<std::string>(_index);
if (boost::iequals(mCurrentClassId, *classId))
if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId))
return;
mCurrentClassId = *classId;
@ -192,7 +191,7 @@ namespace MWGui
mCurrentClassId = id;
mClassList->setIndexSelected(index);
}
else if (boost::iequals(id, mCurrentClassId))
else if (Misc::StringUtils::ciEqual(id, mCurrentClassId))
{
mClassList->setIndexSelected(index);
}

@ -139,6 +139,7 @@ namespace MWGui
mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed);
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
setCoord(200,0,600,300);
@ -234,11 +235,21 @@ namespace MWGui
mItemView->setModel (mSortModel);
MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model.
setTitle(MWWorld::Class::get(container).getName(container));
}
void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Space)
onCloseButtonClicked(mCloseButton);
if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter)
onTakeAllButtonClicked(mTakeButton);
}
void ContainerWindow::close()
{
WindowBase::close();
@ -338,6 +349,8 @@ namespace MWGui
mPickpocketDetected = true;
return false;
}
else
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1);
}
else
{

@ -75,6 +75,7 @@ namespace MWGui
void onCloseButtonClicked(MyGUI::Widget* _sender);
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char);
/// @return is taking the item allowed?
bool onTakeItem(const ItemStack& item, int count);

@ -545,7 +545,7 @@ namespace MWGui
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{
std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::lowerCase(item) == title)
if (Misc::StringUtils::ciEqual(item, title))
{
realTitle = item;
break;

@ -257,7 +257,7 @@ namespace MWGui
{
if (mEffects.size() <= 0)
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}");
MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu11}");
return;
}

@ -267,7 +267,8 @@ namespace MWGui
else if ((mode == GM_Container) || (mode == GM_Inventory))
{
// pick up object
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
if (!object.isEmpty())
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
}
}
}

@ -13,6 +13,8 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/action.hpp"
#include "../mwscript/interpretercontext.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "bookwindow.hpp"
#include "scrollwindow.hpp"
@ -351,6 +353,48 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned);
}
void InventoryWindow::useItem(const MWWorld::Ptr &ptr)
{
const std::string& script = ptr.getClass().getScript(ptr);
// If the item has a script, set its OnPcEquip to 1
if (!script.empty()
// Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards,
// the next time it is equipped will work normally, but will not set onpcequip
&& (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1))
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Give the script a chance to run once before we do anything else
// this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this)
if (!script.empty())
{
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
}
if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0)
{
boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(ptr).use(ptr);
action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr());
// this is necessary for books/scrolls: if they are already in the player's inventory,
// the "Take" button should not be visible.
// NOTE: the take button is "reset" when the window opens, so we can safely do the following
// without screwing up future book windows
MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false);
MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false);
mSkippedToEquip = MWWorld::Ptr();
}
else
mSkippedToEquip = ptr;
mItemView->update();
notifyContentChanged();
}
void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)
{
if (mDragAndDrop->mIsOnDragAndDrop)
@ -369,21 +413,7 @@ namespace MWGui
mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount);
ptr = *it;
}
boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(ptr).use(ptr);
action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr());
// this is necessary for books/scrolls: if they are already in the player's inventory,
// the "Take" button should not be visible.
// NOTE: the take button is "reset" when the window opens, so we can safely do the following
// without screwing up future book windows
MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false);
MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false);
mItemView->update();
notifyContentChanged();
useItem(ptr);
}
else
{

@ -46,6 +46,8 @@ namespace MWGui
void updatePlayer();
void useItem(const MWWorld::Ptr& ptr);
void setGuiMode(GuiMode mode);
private:
@ -74,6 +76,8 @@ namespace MWGui
MyGUI::Button* mFilterMagic;
MyGUI::Button* mFilterMisc;
MWWorld::Ptr mSkippedToEquip;
GuiMode mGuiMode;
int mLastXSize;

@ -8,13 +8,12 @@
namespace MWGui
{
MessageBoxManager::MessageBoxManager ()
MessageBoxManager::MessageBoxManager (float timePerChar)
{
// TODO: fMessageTimePerChar
mMessageBoxSpeed = 0.1;
mInterMessageBoxe = NULL;
mStaticMessageBox = NULL;
mLastButtonPressed = -1;
mMessageBoxSpeed = timePerChar;
}
MessageBoxManager::~MessageBoxManager ()
@ -63,7 +62,8 @@ namespace MWGui
{
MessageBox *box = new MessageBox(*this, message);
box->mCurrentTime = 0;
box->mMaxTime = message.length()*mMessageBoxSpeed;
std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message);
box->mMaxTime = realMessage.length()*mMessageBoxSpeed;
if(stat)
mStaticMessageBox = box;
@ -127,12 +127,6 @@ namespace MWGui
mMessageBoxSpeed = speed;
}
void MessageBoxManager::okayPressed ()
{
if(mInterMessageBoxe != NULL)
mInterMessageBoxe->okayPressed();
}
int MessageBoxManager::readPressedButton ()
{
int pressed = mLastButtonPressed;
@ -333,23 +327,25 @@ namespace MWGui
}
}
}
void InteractiveMessageBox::okayPressed()
{
// Set key focus to "Ok" button
std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
std::vector<MyGUI::Button*>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok)
if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok))
{
buttonActivated(*button);
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
MyGUI::InputManager::getInstance().setKeyFocusWidget(*button);
(*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed);
break;
}
}
}
void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space)
buttonActivated(_sender);
}
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)

@ -22,7 +22,7 @@ namespace MWGui
class MessageBoxManager
{
public:
MessageBoxManager ();
MessageBoxManager (float timePerChar);
~MessageBoxManager ();
void onFrame (float frameDuration);
void createMessageBox (const std::string& message, bool stat = false);
@ -33,7 +33,6 @@ namespace MWGui
bool removeMessageBox (MessageBox *msgbox);
void setMessageBoxSpeed (int speed);
void okayPressed();
int readPressedButton ();
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@ -74,7 +73,6 @@ namespace MWGui
{
public:
InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
void okayPressed ();
void mousePressed (MyGUI::Widget* _widget);
int readPressedButton ();
@ -82,6 +80,7 @@ namespace MWGui
private:
void buttonActivated (MyGUI::Widget* _widget);
void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char);
MessageBoxManager& mMessageBoxManager;
MyGUI::EditBox* mMessageWidget;

@ -9,7 +9,7 @@ namespace MWGui
PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel)
{
mSourceModel = sourceModel;
int chance = MWWorld::Class::get(thief).getNpcStats(thief).getSkill(ESM::Skill::Sneak).getModified();
int chance = thief.getClass().getSkill(thief, ESM::Skill::Sneak);
mSourceModel->update();
for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)

@ -301,6 +301,12 @@ namespace MWGui
if (type == Type_Magic)
{
std::string spellId = button->getChildAt(0)->getUserString("Spell");
// Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (!spells.hasSpell(spellId))
return;
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
}
@ -308,19 +314,7 @@ namespace MWGui
{
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(item).use(item);
action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr());
// this is necessary for books/scrolls: if they are already in the player's inventory,
// the "Take" button should not be visible.
// NOTE: the take button is "reset" when the window opens, so we can safely do the following
// without screwing up future book windows
MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false);
MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false);
// since we changed equipping status, update the inventory window
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item);
}
else if (type == Type_MagicItem)
{

@ -1,6 +1,5 @@
#include "race.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
@ -140,7 +139,7 @@ namespace MWGui
size_t count = mRaceList->getItemCount();
for (size_t i = 0; i < count; ++i)
{
if (boost::iequals(*mRaceList->getItemDataAt<std::string>(i), raceId))
if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt<std::string>(i), raceId))
{
mRaceList->setIndexSelected(i);
MyGUI::Button* okButton;
@ -230,7 +229,7 @@ namespace MWGui
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *raceId = mRaceList->getItemDataAt<std::string>(_index);
if (boost::iequals(mCurrentRaceId, *raceId))
if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId))
return;
mCurrentRaceId = *raceId;
@ -320,7 +319,7 @@ namespace MWGui
continue;
mRaceList->addItem(it->mName, it->mId);
if (boost::iequals(it->mId, mCurrentRaceId))
if (Misc::StringUtils::ciEqual(it->mId, mCurrentRaceId))
mRaceList->setIndexSelected(index);
++index;
}

@ -185,8 +185,8 @@ namespace MWGui
MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill];
if (widget)
{
float modified = value.getModified(), base = value.getBase();
std::string text = boost::lexical_cast<std::string>(std::floor(modified));
int modified = value.getModified(), base = value.getBase();
std::string text = boost::lexical_cast<std::string>(modified);
std::string state = "normal";
if (modified > base)
state = "increased";

@ -296,10 +296,10 @@ namespace MWGui
const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr);
const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player);
float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100);
float a1 = std::min(player.getClass().getSkill(player, ESM::Skill::Mercantile), 100);
float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100);
float d1 = std::min(mPtr.getClass().getSkill(mPtr, ESM::Skill::Mercantile), 100);
float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
@ -318,7 +318,8 @@ namespace MWGui
messageBox("#{sNotifyMessage9}");
int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt();
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition);
if (mPtr.getClass().isNpc())
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition);
return;
}
@ -327,7 +328,8 @@ namespace MWGui
}
int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt();
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition);
if (mPtr.getClass().isNpc())
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition);
// make the item transfer
mTradeModel->transferItems();

@ -130,6 +130,14 @@ namespace MWGui
return;
}
// You can not train a skill above its governing attribute
const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skillId);
if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase())
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage17}");
return;
}
// increase skill
MWWorld::LiveCellRef<ESM::NPC> *playerRef = player.get<ESM::NPC>();
@ -146,6 +154,8 @@ namespace MWGui
// advance time
MWBase::Environment::get().getWorld ()->advanceTime (2);
MWBase::Environment::get().getMechanicsManager()->rest(false);
MWBase::Environment::get().getMechanicsManager()->rest(false);
MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25);
mFadeTimeRemaining = 0.5;

@ -148,7 +148,7 @@ namespace MWGui
int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fTravelTimeMult")->getFloat());
for(int i = 0;i < hours;i++)
{
MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats ();
MWBase::Environment::get().getMechanicsManager ()->rest (true);
}
MWBase::Environment::get().getWorld()->advanceTime(hours);

@ -48,6 +48,7 @@ namespace MWGui
, mRemainingTime(0.05)
, mCurHour(0)
, mManualHours(1)
, mInterruptAt(-1)
{
getWidget(mDateTimeText, "DateTimeText");
getWidget(mRestText, "RestText");
@ -144,43 +145,7 @@ namespace MWGui
void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender)
{
// we need to sleep for a specific time, and since that isn't calculated yet, we'll do it here
// I'm making the assumption here that the # of hours rested is calculated when rest is started
// TODO: the rougher logic here (calculating the hourly deltas) should really go into helper funcs elsewhere
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1;
bool stunted = (stats.getMagicEffects().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0);
float fRestMagicMult = store.get<ESM::GameSetting>().find("fRestMagicMult")->getFloat();
float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
// this massive duplication is why it has to be put into helper functions instead
float fFatigueReturnBase = store.get<ESM::GameSetting>().find("fFatigueReturnBase")->getFloat();
float fFatigueReturnMult = store.get<ESM::GameSetting>().find("fFatigueReturnMult")->getFloat();
float fEndFatigueMult = store.get<ESM::GameSetting>().find("fEndFatigueMult")->getFloat();
float capacity = MWWorld::Class::get(player).getCapacity(player);
float encumbrance = MWWorld::Class::get(player).getEncumbrance(player);
float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
float hourlyFatigueDelta = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
hourlyFatigueDelta *= 3600 * fEndFatigueMult * stats.getAttribute(ESM::Attribute::Endurance).getModified();
float healthHours = hourlyHealthDelta >= 0.0
? (stats.getHealth().getBase() - stats.getHealth().getCurrent()) / hourlyHealthDelta
: 1.0f;
float magickaHours = stunted ? 0.0 :
hourlyMagickaDelta >= 0.0
? (stats.getMagicka().getBase() - stats.getMagicka().getCurrent()) / hourlyMagickaDelta
: 1.0f;
float fatigueHours = hourlyFatigueDelta >= 0.0
? (stats.getFatigue().getBase() - stats.getFatigue().getCurrent()) / hourlyFatigueDelta
: 1.0f;
int autoHours = int(std::ceil( std::max(std::max(healthHours, magickaHours), std::max(fatigueHours, 1.0f)) )); // this should use a variadic max if possible
int autoHours = MWBase::Environment::get().getMechanicsManager()->getHoursToRest();
startWaiting(autoHours);
}
@ -192,7 +157,8 @@ namespace MWGui
void WaitDialog::startWaiting(int hoursToWait)
{
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2);
MWBase::World* world = MWBase::Environment::get().getWorld();
world->getFader ()->fadeOut(0.2);
setVisible(false);
mProgressBar.setVisible (true);
@ -200,6 +166,30 @@ namespace MWGui
mCurHour = 0;
mHours = hoursToWait;
// FIXME: move this somewhere else?
mInterruptAt = -1;
MWWorld::Ptr player = world->getPlayerPtr();
if (mSleeping && player.getCell()->isExterior())
{
std::string regionstr = player.getCell()->mCell->mRegion;
if (!regionstr.empty())
{
const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr);
if (!region->mSleepList.empty())
{
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat();
int x = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested]
float y = fSleepRandMod * hoursToWait;
if (x > y)
{
float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat();
mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait);
mInterruptCreatureList = region->mSleepList;
}
}
}
}
mRemainingTime = 0.05;
mProgressBar.setProgress (0, mHours);
}
@ -242,6 +232,13 @@ namespace MWGui
if (!mWaiting)
return;
if (mCurHour == mInterruptAt)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sSleepInterrupt}");
MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList);
stopWaiting();
}
mRemainingTime -= dt;
while (mRemainingTime < 0)
@ -253,8 +250,7 @@ namespace MWGui
if (mCurHour <= mHours)
{
MWBase::Environment::get().getWorld ()->advanceTime (1);
if (mSleeping)
MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats ();
MWBase::Environment::get().getMechanicsManager ()->rest (mSleeping);
}
}

@ -51,6 +51,9 @@ namespace MWGui
int mManualHours; // stores the hours to rest selected via slider
float mRemainingTime;
int mInterruptAt;
std::string mInterruptCreatureList;
WaitDialogProgressBar mProgressBar;
void onUntilHealedButtonClicked(MyGUI::Widget* sender);

@ -207,7 +207,8 @@ namespace MWGui
mConsole = new Console(w,h, mConsoleOnlyScripts);
trackWindow(mConsole, "console");
mJournal = JournalWindow::create(JournalViewModel::create ());
mMessageBoxManager = new MessageBoxManager();
mMessageBoxManager = new MessageBoxManager(
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMessageTimePerChar")->getFloat());
mInventoryWindow = new InventoryWindow(mDragAndDrop);
mTradeWindow = new TradeWindow();
trackWindow(mTradeWindow, "barter");
@ -676,17 +677,6 @@ namespace MWGui
mMessageBoxManager->removeStaticMessageBox();
}
void WindowManager::enterPressed ()
{
mMessageBoxManager->okayPressed();
}
void WindowManager::activateKeyPressed ()
{
mMessageBoxManager->okayPressed();
mCountDialog->cancel();
}
int WindowManager::readPressedButton ()
{
return mMessageBoxManager->readPressedButton();

@ -222,8 +222,6 @@ namespace MWGui
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), bool showInDialogueModeOnly = false);
virtual void staticMessageBox(const std::string& message);
virtual void removeStaticMessageBox();
virtual void enterPressed ();
virtual void activateKeyPressed ();
virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual void onFrame (float frameDuration);

@ -195,14 +195,7 @@ namespace MWInput
case A_Activate:
resetIdleTime();
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container)
toggleContainer ();
else
MWBase::Environment::get().getWindowManager()->activateKeyPressed();
}
else
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
activate();
break;
case A_Journal:
@ -511,13 +504,6 @@ namespace MWInput
mInputBinder->keyPressed (arg);
if((arg.keysym.sym == SDLK_RETURN || arg.keysym.sym == SDLK_KP_ENTER)
&& MWBase::Environment::get().getWindowManager()->isGuiMode())
{
// Pressing enter when a messagebox is prompting for "ok" will activate the ok button
MWBase::Environment::get().getWindowManager()->enterPressed();
}
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
if (kc != OIS::KC_UNASSIGNED)
@ -730,21 +716,6 @@ namespace MWInput
// .. but don't touch any other mode, except container.
}
void InputManager::toggleContainer()
{
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
if(MWBase::Environment::get().getWindowManager()->isGuiMode())
{
if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container)
MWBase::Environment::get().getWindowManager()->popGuiMode();
else
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container);
}
}
void InputManager::toggleConsole()
{
if (MyGUI::InputManager::getInstance ().isModalAny())

@ -173,7 +173,6 @@ namespace MWInput
void toggleSpell();
void toggleWeapon();
void toggleInventory();
void toggleContainer();
void toggleConsole();
void screenshot();
void toggleJournal();

@ -205,4 +205,22 @@ namespace MWMechanics
}
mSpellsChanged = true;
}
void ActiveSpells::purge(const std::string &actorHandle)
{
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();)
{
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mKey.mId);
if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked
&& it->second.mCasterHandle == actorHandle)
effectIt = it->second.mEffects.erase(effectIt);
else
effectIt++;
}
}
mSpellsChanged = true;
}
}

@ -90,6 +90,9 @@ namespace MWMechanics
/// Remove all active effects, if roll succeeds (for each effect)
void purgeAll (float chance);
/// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle
void purge (const std::string& actorHandle);
bool isSpellActive (std::string id) const;
///< case insensitive

@ -83,6 +83,23 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
return false;
}
void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka)
{
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0;
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
health = 0.1 * endurance;
magicka = 0;
if (!stunted)
{
float fRestMagicMult = settings.find("fRestMagicMult")->getFloat ();
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
}
}
}
@ -200,7 +217,7 @@ namespace MWMechanics
}
// fatigue restoration
calculateRestoration(ptr, duration);
calculateRestoration(ptr, duration, false);
}
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
@ -258,44 +275,37 @@ namespace MWMechanics
creatureStats.setFatigue(fatigue);
}
void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration)
void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep)
{
if (ptr.getClass().getCreatureStats(ptr).isDead())
return;
CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float capacity = MWWorld::Class::get(ptr).getCapacity(ptr);
float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr);
float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (duration == 3600)
if (sleep)
{
// the actor is sleeping, restore health and magicka
float health, magicka;
getRestorationPerHourOfSleep(ptr, health, magicka);
bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0;
DynamicStat<float> stat = stats.getHealth();
stat.setCurrent(stat.getCurrent() + health);
stats.setHealth(stat);
DynamicStat<float> health = stats.getHealth();
health.setCurrent (health.getCurrent() + 0.1 * endurance);
stats.setHealth (health);
stat = stats.getMagicka();
stat.setCurrent(stat.getCurrent() + magicka);
stats.setMagicka(stat);
}
if (!stunted)
{
float fRestMagicMult = settings.find("fRestMagicMult")->getFloat ();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
DynamicStat<float> magicka = stats.getMagicka();
magicka.setCurrent (magicka.getCurrent()
+ fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified());
stats.setMagicka (magicka);
}
}
float capacity = ptr.getClass().getCapacity(ptr);
float encumbrance = ptr.getClass().getEncumbrance(ptr);
float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
// restore fatigue
float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat ();
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat ();
@ -306,6 +316,7 @@ namespace MWMechanics
DynamicStat<float> fatigue = stats.getFatigue();
fatigue.setCurrent (fatigue.getCurrent() + duration * x);
stats.setFatigue (fatigue);
}
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
@ -335,7 +346,7 @@ namespace MWMechanics
float currentDiff = creatureStats.getMagicEffects().get(ESM::MagicEffect::RestoreHealth+i).mMagnitude
- creatureStats.getMagicEffects().get(ESM::MagicEffect::DamageHealth+i).mMagnitude
- creatureStats.getMagicEffects().get(ESM::MagicEffect::AbsorbHealth+i).mMagnitude;
stat.setCurrent(stat.getCurrent() + currentDiff * duration);
stat.setCurrent(stat.getCurrent() + currentDiff * duration, i == 2);
creatureStats.setDynamic(i, stat);
}
@ -505,7 +516,7 @@ namespace MWMechanics
if (magnitude > 0)
{
ESM::Position ipos = ptr.getRefData().getPosition();
Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]);
Ogre::Vector3 pos(ipos.pos);
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
const float distance = 50;
pos = pos + distance*rot.yAxis();
@ -526,7 +537,7 @@ namespace MWMechanics
ref.getPtr().getCellRef().mPos = ipos;
// TODO: Add AI to follow player and fight for him
// TODO: VFX_SummonStart, VFX_SummonEnd
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle()));
}
@ -582,7 +593,8 @@ namespace MWMechanics
if(timeLeft == 0.0f)
{
// If drowning, apply 3 points of damage per second
ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration);
static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->getFloat();
ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - fSuffocationDamage*duration);
// Play a drowning sound as necessary for the player
if(ptr == world->getPlayerPtr())
@ -594,7 +606,10 @@ namespace MWMechanics
}
}
else
stats.setTimeToStartDrowning(20);
{
static const float fHoldBreathTime = world->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->getFloat();
stats.setTimeToStartDrowning(fHoldBreathTime);
}
}
void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration)
@ -811,6 +826,13 @@ namespace MWMechanics
stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0);
// Make sure spell effects with CasterLinked flag are removed
for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(iter->first.getRefData().getHandle());
}
++mDeathCount[cls.getId(iter->first)];
if(cls.isEssential(iter->first))
@ -837,10 +859,28 @@ namespace MWMechanics
}
}
}
void Actors::restoreDynamicStats()
void Actors::restoreDynamicStats(bool sleep)
{
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
calculateRestoration(iter->first, 3600);
calculateRestoration(iter->first, 3600, sleep);
}
int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const
{
float healthPerHour, magickaPerHour;
getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour);
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
float healthHours = healthPerHour >= 0
? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour
: 1.0f;
float magickaHours = magickaPerHour >= 0
? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour
: 1.0f;
int autoHours = std::ceil(std::max(1.f, std::max(healthHours, magickaHours)));
return autoHours;
}
int Actors::countDeaths (const std::string& id) const

@ -36,7 +36,7 @@ namespace MWMechanics
void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration);
void calculateNpcStatModifiers (const MWWorld::Ptr& ptr);
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep);
void updateDrowning (const MWWorld::Ptr& ptr, float duration);
@ -79,8 +79,11 @@ namespace MWMechanics
///< This function is normally called automatically during the update process, but it can
/// also be called explicitly at any time to force an update.
void restoreDynamicStats();
void restoreDynamicStats(bool sleep);
///< If the player is sleeping, this should be called every hour.
int getHoursToRest(const MWWorld::Ptr& ptr) const;
///< Calculate how many hours the given actor needs to rest in order to be fully healed
int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID.

@ -1,21 +1,21 @@
#include "aiactivate.hpp"
#include <iostream>
#include <iostream>
MWMechanics::AiActivate::AiActivate(const std::string &objectId)
: mObjectId(objectId)
{
}
MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
{
return new AiActivate(*this);
}
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{
std::cout << "AiActivate completed.\n";
return true;
}
int MWMechanics::AiActivate::getTypeId() const
{
return 4;
}
MWMechanics::AiActivate::AiActivate(const std::string &objectId)
: mObjectId(objectId)
{
}
MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
{
return new AiActivate(*this);
}
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{
std::cout << "AiActivate completed.\n";
return true;
}
int MWMechanics::AiActivate::getTypeId() const
{
return TypeIdActivate;
}

@ -7,6 +7,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
@ -39,13 +40,13 @@ namespace MWMechanics
if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true;
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
if(actor.getTypeName() == typeid(ESM::NPC).name())
{
MWWorld::Class::get(actor).
MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true);
MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState();
MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
//MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true);
}
ESM::Position pos = actor.getRefData().getPosition();
@ -109,6 +110,17 @@ namespace MWMechanics
}
if( mTimer > 1)
{
if (actor.getClass().isNpc())
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (roll < chance)
{
MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
}
}
MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true);
mTimer = 0;
}
@ -125,7 +137,7 @@ namespace MWMechanics
int AiCombat::getTypeId() const
{
return 5;
return TypeIdCombat;
}
unsigned int AiCombat::getPriority() const
@ -133,6 +145,11 @@ namespace MWMechanics
return 1;
}
const std::string &AiCombat::getTargetId() const
{
return mTargetId;
}
AiCombat *MWMechanics::AiCombat::clone() const
{
return new AiCombat(*this);

@ -23,6 +23,8 @@ namespace MWMechanics
virtual unsigned int getPriority() const;
const std::string &getTargetId() const;
private:
std::string mTargetId;
@ -33,4 +35,4 @@ namespace MWMechanics
};
}
#endif
#endif

@ -169,7 +169,7 @@ namespace MWMechanics
int AiEscort::getTypeId() const
{
return 2;
return TypeIdEscort;
}
}

@ -1,4 +1,4 @@
#include "aifollow.hpp"
#include "aifollow.hpp"
#include <iostream>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
@ -8,18 +8,13 @@
#include <OgreMath.h>
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
{
}
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
{
}
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{
@ -118,3 +113,13 @@ std::string MWMechanics::AiFollow::getFollowedActor()
{
return mActorId;
}
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
int MWMechanics::AiFollow::getTypeId() const
{
return TypeIdFollow;
}

@ -12,7 +12,16 @@ namespace MWMechanics
class AiPackage
{
public:
enum TypeId {
TypeIdNone = -1,
TypeIdWander = 0,
TypeIdTravel = 1,
TypeIdEscort = 2,
TypeIdFollow = 3,
TypeIdActivate = 4,
TypeIdCombat = 5
};
virtual ~AiPackage();
virtual AiPackage *clone() const = 0;
@ -21,7 +30,7 @@ namespace MWMechanics
///< \return Package completed?
virtual int getTypeId() const = 0;
///< 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate
///< @see enum TypeId
virtual unsigned int getPriority() const {return 0;}
///< higher number is higher priority (0 beeing the lowest)

@ -55,6 +55,24 @@ int MWMechanics::AiSequence::getTypeId() const
return mPackages.front()->getTypeId();
}
bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const
{
if (getTypeId() != AiPackage::TypeIdCombat)
return false;
const AiCombat *combat = static_cast<const AiCombat *>(mPackages.front());
targetActorId = combat->getTargetId();
return true;
}
void MWMechanics::AiSequence::stopCombat()
{
while (getTypeId() == AiPackage::TypeIdCombat)
{
delete *mPackages.begin();
mPackages.erase (mPackages.begin());
}
}
bool MWMechanics::AiSequence::isPackageDone() const
{
return mDone;
@ -68,6 +86,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
{
if (mPackages.front()->execute (actor,duration))
{
delete *mPackages.begin();
mPackages.erase (mPackages.begin());
mDone = true;
}
@ -90,7 +109,10 @@ void MWMechanics::AiSequence::stack (const AiPackage& package)
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); it++)
{
if(mPackages.front()->getPriority() <= package.getPriority())
{
mPackages.insert(it,package.clone());
return;
}
}
if(mPackages.empty())
@ -121,7 +143,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
std::vector<int> idles;
for (int i=0; i<8; ++i)
idles.push_back(data.mIdle[i]);
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mUnk);
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat);
}
else if (it->mType == ESM::AI_Escort)
{

@ -34,7 +34,14 @@ namespace MWMechanics
virtual ~AiSequence();
int getTypeId() const;
///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate, 5 Combat
///< @see enum AiPackage::TypeId
bool getCombatTarget (std::string &targetActorId) const;
///< Return true and assign target if combat package is currently
/// active, return false otherwise
void stopCombat();
///< Removes all combat packages until first non-combat or stack empty.
bool isPackageDone() const;
///< Has a package been completed during the last update?

@ -104,7 +104,7 @@ namespace MWMechanics
int AiTravel::getTypeId() const
{
return 1;
return TypeIdTravel;
}
}

@ -6,6 +6,8 @@
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwmechanics/npcstats.hpp"
#include <OgreVector3.h>
@ -64,6 +66,8 @@ namespace MWMechanics
bool AiWander::execute (const MWWorld::Ptr& actor,float duration)
{
if (actor.getClass().isNpc())
actor.getClass().getNpcStats(actor).setDrawState(DrawState_Nothing);
MWBase::World *world = MWBase::Environment::get().getWorld();
if(mDuration)
{
@ -182,6 +186,14 @@ namespace MWMechanics
playIdle(actor, mPlayedIdle);
mChooseAction = false;
mIdleNow = true;
// Play idle voiced dialogue entries randomly
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float chance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
// TODO: do not show subtitle messagebox if player is too far away? or do not say at all?
if (roll < chance)
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
}
}
@ -253,7 +265,7 @@ namespace MWMechanics
int AiWander::getTypeId() const
{
return 0;
return TypeIdWander;
}
void AiWander::stopWalking(const MWWorld::Ptr& actor)

@ -62,7 +62,7 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const
{
bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude);
bool duration = !(flags & ESM::MagicEffect::NoDuration);
bool negative = flags & (ESM::MagicEffect::Negative | ESM::MagicEffect::Harmful);
bool negative = flags & (ESM::MagicEffect::Harmful);
int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic;

@ -157,40 +157,40 @@ public:
void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force)
{
//hit recoils/knockdown animations handling
if(MWWorld::Class::get(mPtr).isActor())
// hit recoils/knockdown animations handling
if(mPtr.getClass().isActor())
{
if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked())
bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery();
bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();
if(mHitState == CharState_None)
{
MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false);
if(mHitState == CharState_None)
if(knockdown)
{
if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr)
&& !MWBase::Environment::get().getWorld()->isSwimming(mPtr) )
{
mHitState = CharState_KnockDown;
mCurrentHit = sHitList[sHitListSize-1];
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
else
mHitState = CharState_KnockDown;
mCurrentHit = sHitList[sHitListSize-1];
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
else if (recovery)
{
mHitState = CharState_Hit;
int iHit = rand() % (sHitListSize-1);
mCurrentHit = sHitList[iHit];
if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit))
{
mHitState = CharState_Hit;
int iHit = rand() % (sHitListSize-1);
//only 3 different hit animations if player is in 1st person
int iHit = rand() % (sHitListSize-3);
mCurrentHit = sHitList[iHit];
if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit))
{
//only 3 different hit animations if player is in 1st person
int iHit = rand() % (sHitListSize-3);
mCurrentHit = sHitList[iHit];
}
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
}
}
else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit))
else if(!mAnimation->isPlaying(mCurrentHit))
{
mCurrentHit.erase();
if (knockdown)
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false);
if (recovery)
mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false);
mHitState = CharState_None;
}
}
@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
{
getWeaponGroup(mWeaponType, mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
mAnimation->showWeapons(true);
}
}
@ -478,9 +479,47 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
mPtr = ptr;
}
bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak)
bool CharacterController::updateCreatureState()
{
const MWWorld::Class &cls = mPtr.getClass();
CreatureStats &stats = cls.getCreatureStats(mPtr);
if(stats.getAttackingOrSpell())
{
if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None)
{
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
switch (stats.getAttackType())
{
case CreatureStats::AT_Chop:
mCurrentWeapon = "attack1";
break;
case CreatureStats::AT_Slash:
mCurrentWeapon = "attack2";
break;
case CreatureStats::AT_Thrust:
mCurrentWeapon = "attack3";
break;
}
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1, "start", "stop",
0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack;
}
}
bool animPlaying = mAnimation->getInfo(mCurrentWeapon);
if (!animPlaying)
mUpperBodyState = UpperCharState_Nothing;
return false;
}
bool CharacterController::updateNpcState(bool inwater, bool isrunning)
{
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
NpcStats &stats = cls.getNpcStats(mPtr);
WeaponType weaptype = WeapType_None;
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
@ -582,8 +621,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
// This has to be done at the start of the casting animation,
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
if (mPtr.getRefData().getHandle() == "player")
stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell());
{
std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();
stats.getSpells().setSelectedSpell(selectedSpell);
}
std::string spellid = stats.getSpells().getSelectedSpell();
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
@ -598,7 +639,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(effectentry.mEffectID);
const ESM::Static* castStatic = store.get<ESM::Static>().find (effect->mCasting);
const ESM::Static* castStatic;
if (!effect->mCasting.empty())
castStatic = store.get<ESM::Static>().find (effect->mCasting);
else
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex);
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Hands");
@ -853,10 +899,11 @@ void CharacterController::update(float duration)
{
bool onground = world->isOnGround(mPtr);
bool inwater = world->isSwimming(mPtr);
bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run);
bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak);
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool flying = world->isFlying(mPtr);
Ogre::Vector3 vec = cls.getMovementVector(mPtr);
vec.normalise();
if(mHitState != CharState_None && mJumpState == JumpState_None)
vec = Ogre::Vector3(0.0f);
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
@ -895,6 +942,41 @@ void CharacterController::update(float duration)
}
}
// reduce fatigue
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
float fatigueLoss = 0;
static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat();
static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat();
static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat();
static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat();
static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat();
static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat();
static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat();
static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat();
const float encumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr);
if (encumbrance < 1)
{
if (sneak)
fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult;
else
{
if (inwater)
{
if (!isrunning)
fatigueLoss = fFatigueSwimWalkBase + encumbrance * fFatigueSwimWalkMult;
else
fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult;
}
if (isrunning)
fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult;
}
}
fatigueLoss *= duration;
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0);
cls.getCreatureStats(mPtr).setFatigue(fatigue);
if(sneak || inwater || flying)
vec.z = 0.0f;
@ -911,8 +993,6 @@ void CharacterController::update(float duration)
cls.getCreatureStats(mPtr).land();
}
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
forcestateupdate = (mJumpState != JumpState_Falling);
mJumpState = JumpState_Falling;
@ -977,14 +1057,16 @@ void CharacterController::update(float duration)
cls.getCreatureStats(mPtr).setHealth(health);
cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true);
// report acrobatics progression
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified();
const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);
if (healthLost > (acrobaticsSkill * fatigueTerm))
{
//TODO: actor falls over
cls.getCreatureStats(mPtr).setKnockedDown(true);
}
else
{
// report acrobatics progression
if (mPtr.getRefData().getHandle() == "player")
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
}
}
}
@ -1047,7 +1129,9 @@ void CharacterController::update(float duration)
}
if(cls.isNpc())
forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate;
forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate;
else
forcestateupdate = updateCreatureState() || forcestateupdate;
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
@ -1080,9 +1164,11 @@ void CharacterController::update(float duration)
else
moved = Ogre::Vector3(0.0f);
// Ensure we're moving in generally the right direction
// Ensure we're moving in generally the right direction...
if(mMovementSpeed > 0.f)
{
float l = moved.length();
if((movement.x < 0.0f && movement.x < moved.x*2.0f) ||
(movement.x > 0.0f && movement.x > moved.x*2.0f))
moved.x = movement.x;
@ -1092,7 +1178,12 @@ void CharacterController::update(float duration)
if((movement.z < 0.0f && movement.z < moved.z*2.0f) ||
(movement.z > 0.0f && movement.z > moved.z*2.0f))
moved.z = movement.z;
// but keep the original speed
float newLength = moved.length();
if (newLength > 0)
moved *= (l / newLength);
}
// Update movement
if(moved.squaredLength() > 1.0f)
world->queueMovement(mPtr, moved);

@ -176,7 +176,8 @@ class CharacterController
void clearAnimQueue();
bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak);
bool updateNpcState(bool inwater, bool isrunning);
bool updateCreatureState();
void updateVisibility();

@ -15,7 +15,8 @@ namespace MWMechanics
mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackType(AT_Chop),
mIsWerewolf(false),
mFallHeight(0), mRecalcDynamicStats(false)
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false),
mMovementFlags(0)
{
for (int i=0; i<4; ++i)
mAiSettings[i] = 0;
@ -207,6 +208,9 @@ namespace MWMechanics
mDynamic[index] = value;
if (index == 2 && value.getCurrent() < 0)
setKnockedDown(true);
if (index==0 && mDynamic[index].getCurrent()<1)
{
if (!mDead)
@ -402,4 +406,50 @@ namespace MWMechanics
}
return false;
}
void CreatureStats::setKnockedDown(bool value)
{
mKnockdown = value;
}
bool CreatureStats::getKnockedDown() const
{
return mKnockdown;
}
void CreatureStats::setHitRecovery(bool value)
{
mHitRecovery = value;
}
bool CreatureStats::getHitRecovery() const
{
return mHitRecovery;
}
bool CreatureStats::getMovementFlag (Flag flag) const
{
return mMovementFlags & flag;
}
void CreatureStats::setMovementFlag (Flag flag, bool state)
{
if (state)
mMovementFlags |= flag;
else
mMovementFlags &= ~flag;
}
bool CreatureStats::getStance(Stance flag) const
{
switch (flag)
{
case Stance_Run:
return getMovementFlag (Flag_Run) || getMovementFlag (Flag_ForceRun);
case Stance_Sneak:
return getMovementFlag (Flag_Sneak) || getMovementFlag (Flag_ForceSneak);
}
return false; // shut up, compiler
}
}

@ -34,7 +34,10 @@ namespace MWMechanics
bool mAlarmed;
bool mAttacked;
bool mHostile;
bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not.
bool mAttackingOrSpell;
bool mKnockdown;
bool mHitRecovery;
unsigned int mMovementFlags;
float mFallHeight;
@ -45,6 +48,7 @@ namespace MWMechanics
// Do we need to recalculate stats derived from attributes or other factors?
bool mRecalcDynamicStats;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
protected:
bool mIsWerewolf;
@ -112,9 +116,9 @@ namespace MWMechanics
enum AttackType
{
AT_Chop,
AT_Slash,
AT_Thrust,
AT_Chop
AT_Thrust
};
void setAttackType(int attackType) { mAttackType = attackType; }
int getAttackType() { return mAttackType; }
@ -186,6 +190,29 @@ namespace MWMechanics
float getEvasion() const;
void setKnockedDown(bool value);
bool getKnockedDown() const;
void setHitRecovery(bool value);
bool getHitRecovery() const;
enum Flag
{
Flag_ForceRun = 1,
Flag_ForceSneak = 2,
Flag_Run = 4,
Flag_Sneak = 8
};
enum Stance
{
Stance_Run,
Stance_Sneak
};
bool getMovementFlag (Flag flag) const;
void setMovementFlag (Flag flag, bool state);
/// Like getMovementFlag, but also takes into account if the flag is Forced
bool getStance (Stance flag) const;
void setLastHitObject(const std::string &objectid);
const std::string &getLastHitObject() const;

@ -6,7 +6,6 @@
#include "creaturestats.hpp"
#include "npcstats.hpp"
#include <boost/algorithm/string.hpp>
namespace MWMechanics
{
@ -60,7 +59,7 @@ namespace MWMechanics
store.remove(mSoulGemPtr, 1, player);
//Exception for Azura Star, new one will be added after enchanting
if(boost::iequals(mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
if(Misc::StringUtils::ciEqual(mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
store.add("Misc_SoulGem_Azura", 1, player);
if(mSelfEnchanting)

@ -0,0 +1,82 @@
#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H
#define OPENMW_MECHANICS_LEVELLEDLIST_H
#include "../mwworld/ptr.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWMechanics
{
/// @return ID of resulting item, or empty if none
inline std::string getLevelledItem (const ESM::LeveledListBase* levItem, bool creature, unsigned char failChance=0)
{
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr();
int playerLevel = player.getClass().getCreatureStats(player).getLevel();
failChance += levItem->mChanceNone;
int random = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (random < failChance)
return std::string();
std::vector<std::string> candidates;
int highestLevel = 0;
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
{
if (it->mLevel > highestLevel && it->mLevel <= playerLevel)
highestLevel = it->mLevel;
}
// For levelled creatures, the flags are swapped. This file format just makes so much sense.
bool allLevels = levItem->mFlags & ESM::ItemLevList::AllLevels;
if (creature)
allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels;
std::pair<int, std::string> highest = std::make_pair(-1, "");
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
{
if (playerLevel >= it->mLevel
&& (allLevels || it->mLevel == highestLevel))
{
candidates.push_back(it->mId);
if (it->mLevel >= highest.first)
highest = std::make_pair(it->mLevel, it->mId);
}
}
if (candidates.empty())
return std::string();
std::string item = candidates[std::rand()%candidates.size()];
// Is this another levelled item or a real item?
try
{
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, 1);
if (ref.getPtr().getTypeName() != typeid(ESM::ItemLevList).name()
&& ref.getPtr().getTypeName() != typeid(ESM::CreatureLevList).name())
{
return item;
}
else
{
if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, failChance);
else
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, failChance);
}
}
catch (std::logic_error& e)
{
// Vanilla doesn't fail on nonexistent items in levelled lists
std::cerr << "Warning: ignoring nonexistent item '" << item << "'" << std::endl;
return std::string();
}
}
}
#endif

@ -19,7 +19,7 @@
namespace
{
/// @return is \a ptr allowed to take/use \a item or is it a crime?
bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item)
bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim)
{
const std::string& owner = item.getCellRef().mOwner;
bool isOwned = !owner.empty();
@ -33,6 +33,9 @@ namespace
isFactionOwned = true;
}
if (!item.getCellRef().mOwner.empty())
victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().mOwner, true);
return (!isOwned && !isFactionOwned);
}
}
@ -367,9 +370,14 @@ namespace MWMechanics
mObjects.update(duration, paused);
}
void MechanicsManager::restoreDynamicStats()
void MechanicsManager::rest(bool sleep)
{
mActors.restoreDynamicStats ();
mActors.restoreDynamicStats (sleep);
}
int MechanicsManager::getHoursToRest() const
{
return mActors.getHoursToRest(mWatched);
}
void MechanicsManager::setPlayerName (const std::string& name)
@ -464,21 +472,23 @@ namespace MWMechanics
std::string npcFaction = "";
if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first;
if (playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerStats.getFactionRanks().end())
Misc::StringUtils::toLower(npcFaction);
if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end())
{
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin();
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it)
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.begin();
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.end(); ++it)
{
if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction)
if(Misc::StringUtils::ciEqual(it->mFaction, npcFaction)
&& !playerStats.getExpelled(it->mFaction))
reaction = it->mReaction;
}
rank = playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second;
rank = playerStats.getFactionRanks().find(npcFaction)->second;
}
else if (npcFaction != "")
{
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin();
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();++it)
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.begin();
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.end();++it)
{
if(playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerStats.getFactionRanks().end() )
{
@ -752,11 +762,9 @@ namespace MWMechanics
bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed)
{
if (isAllowedToUse(ptr, bed))
return false;
MWWorld::Ptr victim;
if (!bed.getCellRef().mOwner.empty())
victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true);
if (isAllowedToUse(ptr, bed, victim))
return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
{
@ -767,20 +775,26 @@ namespace MWMechanics
return false;
}
void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count)
void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
{
if (isAllowedToUse(ptr, item))
return;
MWWorld::Ptr victim;
if (!item.getCellRef().mOwner.empty())
victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true);
if (isAllowedToUse(ptr, item, victim))
return;
commitCrime(ptr, victim, OT_Trespassing);
}
void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count)
{
MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item, victim))
return;
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count);
}
bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg)
{
// TODO: expell from faction
if (ptr.getRefData().getHandle() != "player")
return false;
bool reported=false;
for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it)
@ -797,10 +811,7 @@ namespace MWMechanics
// Actor has witnessed a crime. Will he report it?
// (not sure, is > 0 correct?)
if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0
// This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone
&& (type != OT_Assault || it->first != victim)
)
if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0)
{
// TODO: stats.setAlarmed(true) on NPCs within earshot
// fAlarmRadius ?
@ -830,10 +841,32 @@ namespace MWMechanics
else if (type == OT_Theft)
arg *= store.find("fCrimeStealing")->getFloat();
// TODO: In some cases (type == Assault), if no NPCs are within earshot, the report will have no effect.
// however other crime types seem to be always produce a bounty.
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
+ arg);
if (!victim.isEmpty())
{
int fight = 0;
// Increase in fight rating for each type of crime
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
fight = store.find("iFightTrespass")->getFloat();
else if (type == OT_Pickpocket)
fight = store.find("iFightPickpocket")->getInt();
else if (type == OT_Assault)
fight = store.find("iFightAttack")->getInt();
else if (type == OT_Murder)
fight = store.find("iFightKilling")->getInt();
else if (type == OT_Theft)
fight = store.find("fFightStealing")->getFloat();
// Not sure if this should be permanent?
fight = victim.getClass().getCreatureStats(victim).getAiSetting(CreatureStats::AI_Fight).getBase() + fight;
victim.getClass().getCreatureStats(victim).setAiSetting(CreatureStats::AI_Fight, fight);
}
// If committing a crime against a faction member, expell from the faction
if (!victim.isEmpty() && victim.getClass().isNpc())
{
@ -851,6 +884,9 @@ namespace MWMechanics
bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
{
if (observer.getClass().getCreatureStats(observer).isDead())
return false;
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
@ -860,15 +896,13 @@ namespace MWMechanics
return false;
float sneakTerm = 0;
if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)
if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak)
&& !MWBase::Environment::get().getWorld()->isSwimming(ptr)
&& MWBase::Environment::get().getWorld()->isOnGround(ptr))
{
static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->getFloat();
float sneak = 0;
if (ptr.getClass().isNpc())
sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified();
float sneak = ptr.getClass().getSkill(ptr, ESM::Skill::Sneak);
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0;
@ -896,9 +930,7 @@ namespace MWMechanics
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude;
int obsSneak = 0;
if (observer.getClass().isNpc())
obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified();
int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak);
float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind;

@ -81,8 +81,12 @@ namespace MWMechanics
virtual void setPlayerClass (const ESM::Class& class_);
///< Set player class to custom class.
virtual void restoreDynamicStats();
///< If the player is sleeping, this should be called every hour.
virtual void rest(bool sleep);
///< If the player is sleeping or waiting, this should be called every hour.
/// @param sleep is the player sleeping or waiting?
virtual int getHoursToRest() const;
///< Calculate how many hours the player needs to rest in order to be fully healed
virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying);
///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.
@ -113,6 +117,8 @@ namespace MWMechanics
OffenseType type, int arg=0);
/// Utility to check if taking this item is illegal and calling commitCrime if so
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count);
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item);
/// Attempt sleeping in a bed. If this is illegal, call commitCrime.
/// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed);

@ -22,8 +22,7 @@
#include "../mwbase/soundmanager.hpp"
MWMechanics::NpcStats::NpcStats()
: mMovementFlags (0)
, mDrawState (DrawState_Nothing)
: mDrawState (DrawState_Nothing)
, mBounty (0)
, mLevelProgress(0)
, mDisposition(0)
@ -34,9 +33,7 @@ MWMechanics::NpcStats::NpcStats()
, mTimeToStartDrowning(20.0)
, mLastDrowningHit(0)
{
mSkillIncreases.resize (ESM::Attribute::Length);
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0;
mSkillIncreases.resize (ESM::Attribute::Length, 0);
}
MWMechanics::DrawState_ MWMechanics::NpcStats::getDrawState() const
@ -69,19 +66,6 @@ void MWMechanics::NpcStats::setBaseDisposition(int disposition)
mDisposition = disposition;
}
bool MWMechanics::NpcStats::getMovementFlag (Flag flag) const
{
return mMovementFlags & flag;
}
void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state)
{
if (state)
mMovementFlags |= flag;
else
mMovementFlags &= ~flag;
}
const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const
{
if (index<0 || index>=ESM::Skill::Length)
@ -207,7 +191,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_,
if(mIsWerewolf)
return;
MWMechanics::SkillValue value = getSkill (skillIndex);
MWMechanics::SkillValue& value = getSkill (skillIndex);
value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType));

@ -25,18 +25,6 @@ namespace MWMechanics
class NpcStats : public CreatureStats
{
public:
enum Flag
{
Flag_ForceRun = 1,
Flag_ForceSneak = 2,
Flag_Run = 4,
Flag_Sneak = 8
};
private:
/// NPCs other than the player can only have one faction. But for the sake of consistency
/// we use the same data structure for the PC and the NPCs.
/// \note the faction key must be in lowercase
@ -44,7 +32,6 @@ namespace MWMechanics
DrawState_ mDrawState;
int mDisposition;
unsigned int mMovementFlags;
SkillValue mSkill[27];
SkillValue mWerewolfSkill[27];
int mBounty;
@ -89,10 +76,6 @@ namespace MWMechanics
void setReputation(int reputation);
bool getMovementFlag (Flag flag) const;
void setMovementFlag (Flag flag, bool state);
const SkillValue& getSkill (int index) const;
SkillValue& getSkill (int index);

@ -19,7 +19,7 @@ namespace MWMechanics
NpcStats& stats = ptr.getClass().getNpcStats(ptr);
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float sneak = stats.getSkill(ESM::Skill::Sneak).getModified();
float sneak = ptr.getClass().getSkill(ptr, ESM::Skill::Sneak);
return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm();
}
@ -30,8 +30,7 @@ namespace MWMechanics
float t = 2*x - y;
NpcStats& pcStats = mThief.getClass().getNpcStats(mThief);
float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified();
float pcSneak = mThief.getClass().getSkill(mThief, ESM::Skill::Sneak);
int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iPickMinChance")->getInt();
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()

@ -6,6 +6,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "npcstats.hpp"
#include "creaturestats.hpp"
@ -45,6 +46,7 @@ namespace MWMechanics
resultMessage = "#{sLockImpossible}";
else
{
MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock);
int roll = static_cast<float> (std::rand()) / RAND_MAX * 100;
if (roll <= x)
{
@ -86,6 +88,7 @@ namespace MWMechanics
resultMessage = "#{sTrapImpossible}";
else
{
MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap);
int roll = static_cast<float> (std::rand()) / RAND_MAX * 100;
if (roll <= x)
{

@ -4,7 +4,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/actionteleport.hpp"
@ -57,6 +57,7 @@ namespace MWMechanics
ESM::EffectList reflectedEffects;
std::vector<ActiveSpells::Effect> appliedLastingEffects;
bool firstAppliedEffect = true;
bool anyHarmfulEffect = false;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
effectIt!=effects.mList.end(); ++effectIt)
@ -77,6 +78,8 @@ namespace MWMechanics
float magnitudeMult = 1;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor())
{
anyHarmfulEffect = true;
// If player is attempting to cast a harmful spell, show the target's HP bar
if (caster.getRefData().getHandle() == "player" && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target);
@ -161,13 +164,14 @@ namespace MWMechanics
ActiveSpells::Effect effect_ = effect;
effect_.mMagnitude *= -1;
effects.push_back(effect_);
// Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
effects, mSourceName, caster.getRefData().getHandle());
effects, mSourceName, target.getRefData().getHandle());
}
}
}
else
applyInstantEffect(target, EffectKey(*effectIt), magnitude);
applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);
// HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent.
// This was probably just done to have the effect visible in the magic menu for a while
@ -177,7 +181,7 @@ namespace MWMechanics
|| effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute
|| effectIt->mEffectID == ESM::MagicEffect::RestoreSkill
)
applyInstantEffect(target, EffectKey(*effectIt), magnitude);
applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);
if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
{
@ -197,15 +201,17 @@ namespace MWMechanics
}
// Add VFX
const ESM::Static* castStatic;
if (!magicEffect->mHit.empty())
{
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
if (anim)
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
}
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
if (anim)
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
}
// TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.
@ -218,9 +224,13 @@ namespace MWMechanics
if (appliedLastingEffects.size())
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
mSourceName, caster.getRefData().getHandle());
if (anyHarmfulEffect && target.getClass().isActor() && target != caster
&& target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault);
}
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude)
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude)
{
short effectId = effect.mId;
if (!target.getClass().isActor())
@ -232,11 +242,13 @@ namespace MWMechanics
}
else if (effectId == ESM::MagicEffect::Open)
{
// TODO: This is a crime
if (target.getCellRef().mLockLevel <= magnitude)
{
if (target.getCellRef().mLockLevel > 0)
{
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
}
target.getCellRef().mLockLevel = 0;
}
else
@ -426,8 +438,7 @@ namespace MWMechanics
DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster);
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss));
stats.setFatigue(fatigue);
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);
bool fail = false;

@ -16,15 +16,15 @@
namespace MWMechanics
{
inline int spellSchoolToSkill(int school)
inline ESM::Skill::SkillEnum spellSchoolToSkill(int school)
{
std::map<int, int> schoolSkillMap; // maps spell school to skill id
schoolSkillMap[0] = 11; // alteration
schoolSkillMap[1] = 13; // conjuration
schoolSkillMap[3] = 12; // illusion
schoolSkillMap[2] = 10; // destruction
schoolSkillMap[4] = 14; // mysticism
schoolSkillMap[5] = 15; // restoration
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
schoolSkillMap[0] = ESM::Skill::Alteration;
schoolSkillMap[1] = ESM::Skill::Conjuration;
schoolSkillMap[3] = ESM::Skill::Illusion;
schoolSkillMap[2] = ESM::Skill::Destruction;
schoolSkillMap[4] = ESM::Skill::Mysticism;
schoolSkillMap[5] = ESM::Skill::Restoration;
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
return schoolSkillMap[school];
}
@ -38,10 +38,9 @@ namespace MWMechanics
*/
inline float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL)
{
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
if (creatureStats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude)
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).mMagnitude)
return 0;
float y = FLT_MAX;
@ -63,7 +62,7 @@ namespace MWMechanics
"fEffectCostMult")->getFloat();
x *= fEffectCostMult;
float s = 2 * stats.getSkill(spellSchoolToSkill(magicEffect->mData.mSchool)).getModified();
float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool));
if (s - x < y)
{
y = s - x;
@ -203,7 +202,7 @@ namespace MWMechanics
void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false);
void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude);
void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude);
};
}

@ -44,6 +44,8 @@ namespace MWMechanics
TIterator end() const;
bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); }
void add (const std::string& spell);
///< Adding a spell that is already listed in *this is a no-op.

@ -246,6 +246,18 @@ namespace MWMechanics
{
return !(left == right);
}
inline bool operator== (const SkillValue& left, const SkillValue& right)
{
return left.getBase() == right.getBase()
&& left.getModifier() == right.getModifier()
&& left.getDamage() == right.getDamage()
&& left.getProgress() == right.getProgress();
}
inline bool operator!= (const SkillValue& left, const SkillValue& right)
{
return !(left == right);
}
}
#endif

@ -30,7 +30,7 @@
namespace MWRender
{
Ogre::Real Animation::AnimationValue::getValue() const
Ogre::Real Animation::AnimationTime::getValue() const
{
AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName);
if(iter != mAnimation->mStates.end())
@ -38,16 +38,16 @@ Ogre::Real Animation::AnimationValue::getValue() const
return 0.0f;
}
void Animation::AnimationValue::setValue(Ogre::Real)
void Animation::AnimationTime::setValue(Ogre::Real)
{
}
Ogre::Real Animation::EffectAnimationValue::getValue() const
Ogre::Real Animation::EffectAnimationTime::getValue() const
{
return mTime;
}
void Animation::EffectAnimationValue::setValue(Ogre::Real)
void Animation::EffectAnimationTime::setValue(Ogre::Real)
{
}
@ -60,10 +60,10 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
, mNonAccumRoot(NULL)
, mNonAccumCtrl(NULL)
, mAccumulate(0.0f)
, mNullAnimationValuePtr(OGRE_NEW NullAnimationValue)
, mNullAnimationTimePtr(OGRE_NEW NullAnimationTime)
{
for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this));
mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this));
}
Animation::~Animation()
@ -139,7 +139,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly)
for(size_t i = 0;i < mObjectRoot->mControllers.size();i++)
{
if(mObjectRoot->mControllers[i].getSource().isNull())
mObjectRoot->mControllers[i].setSource(mAnimationValuePtr[0]);
mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]);
}
}
@ -286,7 +286,7 @@ void Animation::addAnimSource(const std::string &model)
}
}
ctrls[i].setSource(mAnimationValuePtr[grp]);
ctrls[i].setSource(mAnimationTimePtr[grp]);
grpctrls[grp].push_back(ctrls[i]);
}
}
@ -296,7 +296,7 @@ void Animation::clearAnimSources()
mStates.clear();
for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i]->setAnimName(std::string());
mAnimationTimePtr[i]->setAnimName(std::string());
mNonAccumCtrl = NULL;
@ -660,13 +660,22 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
else if(evt.compare(off, len, "unequip detach") == 0)
showWeapons(false);
else if(evt.compare(off, len, "chop hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
else if(evt.compare(off, len, "slash hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
else if(evt.compare(off, len, "thrust hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
else if(evt.compare(off, len, "hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr);
{
if (groupname == "attack1")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
else if (groupname == "attack2")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
else if (groupname == "attack3")
mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
else
mPtr.getClass().hit(mPtr);
}
else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release")
MWBase::Environment::get().getWorld()->castSpell(mPtr);
@ -789,7 +798,7 @@ void Animation::resetActiveGroups()
active = state;
}
mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ?
mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ?
std::string() : active->first);
}
mNonAccumCtrl = NULL;
@ -797,7 +806,7 @@ void Animation::resetActiveGroups()
if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f))
return;
AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName());
AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName());
if(state == mStates.end())
return;
@ -869,13 +878,13 @@ Ogre::Vector3 Animation::runAnimation(float duration)
targetTime = state.mTime + timepassed;
if(textkey == textkeys.end() || textkey->first > targetTime)
{
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
updatePosition(state.mTime, targetTime, movement);
state.mTime = std::min(targetTime, state.mStopTime);
}
else
{
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName())
updatePosition(state.mTime, textkey->first, movement);
state.mTime = textkey->first;
}
@ -926,7 +935,7 @@ Ogre::Vector3 Animation::runAnimation(float duration)
// Apply group controllers
for(size_t grp = 0;grp < sNumGroups;grp++)
{
const std::string &name = mAnimationValuePtr[grp]->getAnimName();
const std::string &name = mAnimationTimePtr[grp]->getAnimName();
if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end())
{
const Ogre::SharedPtr<AnimSource> &src = stateiter->second.mSource;
@ -1042,6 +1051,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
else
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
// TODO: turn off shadow casting
setRenderProperties(params.mObjects, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
@ -1052,7 +1062,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for(size_t i = 0;i < params.mObjects->mControllers.size();i++)
{
if(params.mObjects->mControllers[i].getSource().isNull())
params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationValue> (new EffectAnimationValue()));
params.mObjects->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
}
if (!texture.empty())
@ -1110,7 +1120,7 @@ void Animation::updateEffects(float duration)
NifOgre::ObjectScenePtr objects = it->mObjects;
for(size_t i = 0; i < objects->mControllers.size() ;i++)
{
EffectAnimationValue* value = dynamic_cast<EffectAnimationValue*>(objects->mControllers[i].getSource().get());
EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value)
value->addTime(duration);
@ -1125,7 +1135,7 @@ void Animation::updateEffects(float duration)
float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength;
for(size_t i = 0; i < objects->mControllers.size() ;i++)
{
EffectAnimationValue* value = dynamic_cast<EffectAnimationValue*>(objects->mControllers[i].getSource().get());
EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value)
value->resetTime(remainder);
}

@ -32,14 +32,14 @@ protected:
/* This is the number of *discrete* groups. */
static const size_t sNumGroups = 4;
class AnimationValue : public Ogre::ControllerValue<Ogre::Real>
class AnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
Animation *mAnimation;
std::string mAnimationName;
public:
AnimationValue(Animation *anim)
AnimationTime(Animation *anim)
: mAnimation(anim)
{ }
@ -52,12 +52,12 @@ protected:
virtual void setValue(Ogre::Real value);
};
class EffectAnimationValue : public Ogre::ControllerValue<Ogre::Real>
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationValue() : mTime(0) { }
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
void resetTime(float value) { mTime = value; }
@ -67,7 +67,7 @@ protected:
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real>
class NullAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
public:
virtual Ogre::Real getValue() const
@ -134,8 +134,8 @@ protected:
AnimStateMap mStates;
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups];
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr;
Ogre::SharedPtr<AnimationTime> mAnimationTimePtr[sNumGroups];
Ogre::SharedPtr<NullAnimationTime> mNullAnimationTimePtr;
ObjectAttachMap mAttachedObjects;
@ -189,16 +189,18 @@ protected:
/** Adds an additional light to the given object list using the specified ESM record. */
void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light);
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
void clearAnimSources();
// TODO: Should not be here
Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item);
public:
// FIXME: Move outside of this class
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation();

@ -226,11 +226,10 @@ namespace MWRender
mCamera->setPosition(0.f, 0.f, offset);
}
void Camera::setSneakOffset()
void Camera::setSneakOffset(float offset)
{
// TODO: iFirstPersonSneakDelta
if(mAnimation)
mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f));
mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -offset));
}
float Camera::getYaw()

@ -87,7 +87,7 @@ namespace MWRender
/// As animation is tied to the camera, this needs
/// to be set each frame after the animation is
/// applied.
void setSneakOffset();
void setSneakOffset(float offset);
bool isFirstPerson() const
{ return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }

@ -4,6 +4,7 @@
#include <OgreSceneManager.h>
#include <OgreRoot.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreCamera.h>
#include <libs/openengine/ogre/selectionbuffer.hpp>

@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
if((ref->mBase->mFlags&ESM::Creature::Biped))
if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\base_anim.nif");
addAnimSource(model);
}

@ -0,0 +1,117 @@
#include "effectmanager.hpp"
#include <OgreSceneManager.h>
#include <OgreParticleSystem.h>
#include "animation.hpp"
#include "renderconst.hpp"
namespace MWRender
{
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
virtual Ogre::Real getValue() const { return mTime; }
virtual void setValue(Ogre::Real value) {}
};
EffectManager::EffectManager(Ogre::SceneManager *sceneMgr)
: mSceneMgr(sceneMgr)
{
}
void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition)
{
Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition);
// fix texture extension to .dds
if (textureOverride.size() > 4)
{
textureOverride[textureOverride.size()-3] = 'd';
textureOverride[textureOverride.size()-2] = 'd';
textureOverride[textureOverride.size()-1] = 's';
}
NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
// TODO: turn off shadow casting
MWRender::Animation::setRenderProperties(scene, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
for(size_t i = 0;i < scene->mControllers.size();i++)
{
if(scene->mControllers[i].getSource().isNull())
scene->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
}
if (!textureOverride.empty())
{
for(size_t i = 0;i < scene->mParticles.size(); ++i)
{
Ogre::ParticleSystem* partSys = scene->mParticles[i];
Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(partSys);
for (int t=0; t<mat->getNumTechniques(); ++t)
{
Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{
Ogre::Pass* pass = tech->getPass(p);
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
tus->setTextureName("textures\\" + textureOverride);
}
}
}
}
}
mEffects.push_back(std::make_pair(sceneNode, scene));
}
void EffectManager::update(float dt)
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
NifOgre::ObjectScenePtr objects = it->second;
for(size_t i = 0; i < objects->mControllers.size() ;i++)
{
EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value)
value->addTime(dt);
objects->mControllers[i].update();
}
// Finished playing?
if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength)
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
continue;
}
++it;
}
}
void EffectManager::clear()
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
}
}
}

@ -0,0 +1,31 @@
#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H
#define OPENMW_MWRENDER_EFFECTMANAGER_H
#include <components/nifogre/ogrenifloader.hpp>
namespace MWRender
{
// Note: effects attached to another object should be managed by MWRender::Animation::addEffect.
// This class manages "free" effects, i.e. attached to a dedicated scene node in the world.
class EffectManager
{
public:
EffectManager(Ogre::SceneManager* sceneMgr);
~EffectManager() { clear(); }
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition);
void update(float dt);
/// Remove all effects
void clear();
private:
std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> > mEffects;
Ogre::SceneManager* mSceneMgr;
};
}
#endif

@ -61,8 +61,9 @@ std::string getVampireHead(const std::string& race, bool female)
namespace MWRender
{
float SayAnimationValue::getValue() const
float HeadAnimationTime::getValue() const
{
// TODO: Handle eye blinking (time is in the text keys)
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference))
return 0;
else
@ -124,7 +125,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
{
mNpc = mPtr.get<ESM::NPC>()->mBase;
mSayAnimationValue = Ogre::SharedPtr<SayAnimationValue>(new SayAnimationValue(mPtr));
mHeadAnimationTime = Ogre::SharedPtr<HeadAnimationTime>(new HeadAnimationTime(mPtr));
for(size_t i = 0;i < ESM::PRT_Count;i++)
{
@ -595,10 +596,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
{
if(ctrl->getSource().isNull())
{
ctrl->setSource(mNullAnimationValuePtr);
ctrl->setSource(mNullAnimationTimePtr);
if (type == ESM::PRT_Head)
ctrl->setSource(mSayAnimationValue);
ctrl->setSource(mHeadAnimationTime);
}
}

@ -13,12 +13,12 @@ namespace ESM
namespace MWRender
{
class SayAnimationValue : public Ogre::ControllerValue<Ogre::Real>
class HeadAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
MWWorld::Ptr mReference;
public:
SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {}
HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {}
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value)
@ -70,7 +70,7 @@ private:
Ogre::Vector3 mFirstPersonOffset;
Ogre::SharedPtr<SayAnimationValue> mSayAnimationValue;
Ogre::SharedPtr<HeadAnimationTime> mHeadAnimationTime;
float mAlpha;

@ -8,6 +8,7 @@
#include <OgreSubEntity.h>
#include <OgreMeshManager.h>
#include <OgreMaterialManager.h>
#include <OgreCamera.h>
#include "renderconst.hpp"

@ -41,6 +41,7 @@
#include "globalmap.hpp"
#include "videoplayer.hpp"
#include "terrainstorage.hpp"
#include "effectmanager.hpp"
using namespace MWRender;
using namespace Ogre;
@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
, mSunEnabled(0)
, mPhysicsEngine(engine)
, mTerrain(NULL)
, mEffectManager(NULL)
{
mActors = new MWRender::Actors(mRendering, this);
mObjects = new MWRender::Objects(mRendering);
mEffectManager = new EffectManager(mRendering.getScene());
// select best shader mode
bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos);
bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos);
@ -193,6 +196,7 @@ RenderingManager::~RenderingManager ()
delete mVideoPlayer;
delete mActors;
delete mObjects;
delete mEffectManager;
delete mFactory;
}
@ -347,12 +351,14 @@ void RenderingManager::update (float duration, bool paused)
}
// Sink the camera while sneaking
bool isSneaking = MWWorld::Class::get(player).getStance(player, MWWorld::Class::Sneak);
bool isSneaking = player.getClass().getCreatureStats(player).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool isInAir = !world->isOnGround(player);
bool isSwimming = world->isSwimming(player);
static const int i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("i1stPersonSneakDelta")->getInt();
if(isSneaking && !(isSwimming || isInAir))
mCamera->setSneakOffset();
mCamera->setSneakOffset(i1stPersonSneakDelta);
mOcclusionQuery->update(duration);
@ -372,6 +378,8 @@ void RenderingManager::update (float duration, bool paused)
if(paused)
return;
mEffectManager->update(duration);
mActors->update (mRendering.getCamera());
mPlayerAnimation->preRender(mRendering.getCamera());
mObjects->update (duration, mRendering.getCamera());
@ -673,14 +681,14 @@ Shadows* RenderingManager::getShadows()
void RenderingManager::switchToInterior()
{
// causes light flicker in opengl when moving..
//mRendering.getScene()->setCameraRelativeRendering(false);
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
void RenderingManager::switchToExterior()
{
// causes light flicker in opengl when moving..
//mRendering.getScene()->setCameraRelativeRendering(true);
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
@ -1018,4 +1026,9 @@ float RenderingManager::getCameraDistance() const
return mCamera->getCameraDistance();
}
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition)
{
mEffectManager->addEffect(model, texture, worldPosition);
}
} // namespace

@ -21,10 +21,7 @@
namespace Ogre
{
class SceneManager;
class SceneNode;
class Quaternion;
class Vector3;
}
namespace MWWorld
@ -51,6 +48,7 @@ namespace MWRender
class GlobalMap;
class VideoPlayer;
class Animation;
class EffectManager;
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
{
@ -209,6 +207,8 @@ public:
void stopVideo();
void frameStarted(float dt, bool paused);
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition);
protected:
virtual void windowResized(int x, int y);
@ -239,6 +239,8 @@ private:
MWRender::Objects* mObjects;
MWRender::Actors* mActors;
MWRender::EffectManager* mEffectManager;
MWRender::NpcAnimation *mPlayerAnimation;
// 0 normal, 1 more bright, 2 max

@ -8,6 +8,7 @@
#include <OgreShadowCameraSetupLiSPSM.h>
#include <OgreShadowCameraSetupPSSM.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreCamera.h>
#include <extern/shiny/Main/Factory.hpp>

@ -5,6 +5,7 @@
#include <OgreMeshManager.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRoot.h>
#include <OgreCamera.h>
#include "sky.hpp"
#include "renderingmanager.hpp"

@ -16,6 +16,7 @@
#include "../mwmechanics/aifollow.hpp"
#include "../mwmechanics/aitravel.hpp"
#include "../mwmechanics/aiwander.hpp"
#include "../mwmechanics/aicombat.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -401,6 +402,59 @@ namespace MWScript
}
};
template<class R>
class OpGetTarget : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime &runtime)
{
MWWorld::Ptr actor = R()(runtime);
std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
std::string currentTargetId;
bool targetsAreEqual = false;
if (creatureStats.getAiSequence().getCombatTarget (currentTargetId))
{
if (currentTargetId == testedTargetId)
targetsAreEqual = true;
}
runtime.push(int(targetsAreEqual));
}
};
template<class R>
class OpStartCombat : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime &runtime)
{
MWWorld::Ptr actor = R()(runtime);
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actorID));
if (actorID == "player")
creatureStats.setHostile(true);
}
};
template<class R>
class OpStopCombat : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr actor = R()(runtime);
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
creatureStats.getAiSequence().stopCombat();
creatureStats.setHostile(false);
}
};
template<class R>
class OpToggleAI : public Interpreter::Opcode0
{
@ -438,6 +492,12 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetTarget, new OpGetTarget<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeGetTargetExplicit, new OpGetTarget<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeStartCombat, new OpStartCombat<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeStartCombatExplicit, new OpStartCombat<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeStopCombat, new OpStopCombat<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat<ExplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI<ImplicitRef>);
interpreter.installSegment5 (Compiler::Ai::opcodeToggleAIExplicit, new OpToggleAI<ExplicitRef>);

@ -282,7 +282,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
const std::string &name = runtime.getStringLiteral (runtime[0].mInteger);
const std::string &name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr);

@ -76,34 +76,34 @@ namespace MWScript
template<class R>
class OpClearMovementFlag : public Interpreter::Opcode0
{
MWMechanics::NpcStats::Flag mFlag;
MWMechanics::CreatureStats::Flag mFlag;
public:
OpClearMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {}
OpClearMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {}
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, false);
ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, false);
}
};
template<class R>
class OpSetMovementFlag : public Interpreter::Opcode0
{
MWMechanics::NpcStats::Flag mFlag;
MWMechanics::CreatureStats::Flag mFlag;
public:
OpSetMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {}
OpSetMovementFlag (MWMechanics::CreatureStats::Flag flag) : mFlag (flag) {}
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, true);
ptr.getClass().getCreatureStats(ptr).setMovementFlag (mFlag, true);
}
};
@ -116,9 +116,8 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr);
runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun));
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceRun));
}
};
@ -131,9 +130,8 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr);
runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak));
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
runtime.push (stats.getMovementFlag (MWMechanics::CreatureStats::Flag_ForceSneak));
}
};
@ -144,7 +142,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run));
runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run));
}
};
@ -155,7 +153,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak));
runtime.push (ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak));
}
};
@ -172,22 +170,22 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Control::opcodeToggleCollision, new OpToggleCollision);
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRun,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::NpcStats::Flag_ForceRun));
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRun,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::NpcStats::Flag_ForceRun));
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneak,
new OpClearMovementFlag<ImplicitRef> (MWMechanics::NpcStats::Flag_ForceSneak));
new OpClearMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneak,
new OpSetMovementFlag<ImplicitRef> (MWMechanics::NpcStats::Flag_ForceSneak));
new OpSetMovementFlag<ImplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceRunExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::NpcStats::Flag_ForceRun));
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeForceRunExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::NpcStats::Flag_ForceRun));
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceRun));
interpreter.installSegment5 (Compiler::Control::opcodeClearForceSneakExplicit,
new OpClearMovementFlag<ExplicitRef> (MWMechanics::NpcStats::Flag_ForceSneak));
new OpClearMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeForceSneakExplicit,
new OpSetMovementFlag<ExplicitRef> (MWMechanics::NpcStats::Flag_ForceSneak));
new OpSetMovementFlag<ExplicitRef> (MWMechanics::CreatureStats::Flag_ForceSneak));
interpreter.installSegment5 (Compiler::Control::opcodeGetPcRunning, new OpGetPcRunning);
interpreter.installSegment5 (Compiler::Control::opcodeGetPcSneaking, new OpGetPcSneaking);
interpreter.installSegment5 (Compiler::Control::opcodeGetForceRun, new OpGetForceRun<ImplicitRef>);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save