forked from mirror/openmw-tes3mp
commit
35d1502308
142 changed files with 1429 additions and 656 deletions
|
@ -434,7 +434,6 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
|
||||
# Install licenses
|
||||
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
|
||||
ENDIF (DPKG_PROGRAM)
|
||||
|
||||
|
|
|
@ -333,7 +333,7 @@ int load(Arguments& info)
|
|||
|
||||
// Is the user interested in this record type?
|
||||
bool interested = true;
|
||||
if (info.types.size() > 0)
|
||||
if (!info.types.empty())
|
||||
{
|
||||
std::vector<std::string>::iterator match;
|
||||
match = std::find(info.types.begin(), info.types.end(),
|
||||
|
|
|
@ -124,7 +124,7 @@ void printEffectList(ESM::EffectList effects)
|
|||
{
|
||||
int i = 0;
|
||||
std::vector<ESM::ENAMstruct>::iterator eit;
|
||||
for (eit = effects.mList.begin(); eit != effects.mList.end(); eit++)
|
||||
for (eit = effects.mList.begin(); eit != effects.mList.end(); ++eit)
|
||||
{
|
||||
std::cout << " Effect[" << i << "]: " << magicEffectLabel(eit->mEffectID)
|
||||
<< " (" << eit->mEffectID << ")" << std::endl;
|
||||
|
|
|
@ -214,13 +214,13 @@ QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre
|
|||
uint row = 0;
|
||||
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
|
||||
|
||||
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++)
|
||||
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); ++i, ++row)
|
||||
{
|
||||
Ogre::StringVector::iterator opt_it;
|
||||
uint idx = 0;
|
||||
|
||||
for (opt_it = i->second.possibleValues.begin();
|
||||
opt_it != i->second.possibleValues.end(); opt_it++, idx++)
|
||||
opt_it != i->second.possibleValues.end(); ++opt_it, ++idx)
|
||||
{
|
||||
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
|
||||
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
|
||||
|
|
|
@ -121,7 +121,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
|||
//iterate the data directories and add them to the file dialog for loading
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
QString path = QString::fromStdString(iter->string());
|
||||
QString path = QString::fromUtf8 (iter->string().c_str());
|
||||
mFileDialog.addFiles(path);
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -251,13 +251,13 @@ CSMSettings::UserSettings::~UserSettings()
|
|||
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
||||
{
|
||||
mUserFilePath = QString::fromUtf8
|
||||
(mCfgMgr.getUserConfigPath().c_str()) + fileName.toUtf8();
|
||||
(mCfgMgr.getUserConfigPath().string().c_str()) + fileName.toUtf8();
|
||||
|
||||
QString global = QString::fromUtf8
|
||||
(mCfgMgr.getGlobalPath().c_str()) + fileName.toUtf8();
|
||||
(mCfgMgr.getGlobalPath().string().c_str()) + fileName.toUtf8();
|
||||
|
||||
QString local = QString::fromUtf8
|
||||
(mCfgMgr.getLocalPath().c_str()) + fileName.toUtf8();
|
||||
(mCfgMgr.getLocalPath().string().c_str()) + fileName.toUtf8();
|
||||
|
||||
//open user and global streams
|
||||
QTextStream *userStream = openFilestream (mUserFilePath, true);
|
||||
|
|
4
apps/opencs/view/world/datadisplaydelegate.cpp
Executable file → Normal file
4
apps/opencs/view/world/datadisplaydelegate.cpp
Executable file → Normal file
|
@ -25,7 +25,7 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
|
|||
|
||||
void CSVWorld::DataDisplayDelegate::buildPixmaps ()
|
||||
{
|
||||
if (mPixmaps.size() > 0)
|
||||
if (!mPixmaps.empty())
|
||||
mPixmaps.clear();
|
||||
|
||||
IconList::iterator it = mIcons.begin();
|
||||
|
@ -33,7 +33,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps ()
|
|||
while (it != mIcons.end())
|
||||
{
|
||||
mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) );
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
|
|||
{
|
||||
delegate = CommandDelegateFactoryCollection::get().makeDelegate (
|
||||
display, mUndoStack, mParent);
|
||||
mDelegates.insert(std::make_pair<int, CommandDelegate*>(display, delegate));
|
||||
mDelegates.insert(std::make_pair(display, delegate));
|
||||
} else
|
||||
{
|
||||
delegate = delegateIt->second;
|
||||
|
|
|
@ -298,7 +298,7 @@ void CSVWorld::Table::revertRecord()
|
|||
{
|
||||
std::vector<std::string> revertableIds = listRevertableSelectedIds();
|
||||
|
||||
if (revertableIds.size()>0)
|
||||
if (!revertableIds.empty())
|
||||
{
|
||||
if (revertableIds.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
|
||||
|
@ -318,7 +318,7 @@ void CSVWorld::Table::deleteRecord()
|
|||
{
|
||||
std::vector<std::string> deletableIds = listDeletableSelectedIds();
|
||||
|
||||
if (deletableIds.size()>0)
|
||||
if (!deletableIds.empty())
|
||||
{
|
||||
if (deletableIds.size()>1)
|
||||
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
|
||||
|
|
|
@ -67,7 +67,7 @@ add_openmw_dir (mwclass
|
|||
|
||||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||
drawstate spells activespells npcstats aipackage aisequence aipersue alchemy aiwander aitravel aifollow
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow
|
||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||
disease pickpocket levelledlist combat steering obstacle
|
||||
)
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
|
@ -60,7 +65,7 @@ namespace MWBase
|
|||
|
||||
virtual int countSavedGameRecords() const = 0;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) const = 0;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;
|
||||
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
#include "../mwdialogue/topic.hpp"
|
||||
#include "../mwdialogue/quest.hpp"
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
|
@ -80,7 +85,7 @@ namespace MWBase
|
|||
|
||||
virtual int countSavedGameRecords() const = 0;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) const = 0;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;
|
||||
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
||||
};
|
||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
|||
|
||||
virtual void endGame() = 0;
|
||||
|
||||
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
|
||||
|
||||
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
|
||||
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
|
||||
///
|
||||
|
|
|
@ -156,8 +156,9 @@ namespace MWBase
|
|||
virtual void setValue (const std::string& id, int value) = 0;
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time value from [0,20]
|
||||
virtual void setDrowningTimeLeft (float time) =0;
|
||||
/// @param time time left to start drowning
|
||||
/// @param maxTime how long we can be underwater (in total) until drowning starts
|
||||
virtual void setDrowningTimeLeft (float time, float maxTime) = 0;
|
||||
|
||||
virtual void setPlayerClass (const ESM::Class &class_) = 0;
|
||||
///< set current class of player
|
||||
|
@ -302,8 +303,12 @@ namespace MWBase
|
|||
/// Clear all savegame-specific data
|
||||
virtual void clear() = 0;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) = 0;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) = 0;
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
|
||||
virtual int countSavedGameRecords() const = 0;
|
||||
|
||||
/// Does the current stack of GUI-windows permit saving?
|
||||
virtual bool isSavingAllowed() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace MWBase
|
|||
|
||||
virtual int countSavedGameRecords() const = 0;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) const = 0;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
|
||||
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
const std::map<int, int>& contentFileMap) = 0;
|
||||
|
@ -407,6 +407,8 @@ namespace MWBase
|
|||
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
|
||||
///< get Line of Sight (morrowind stupid implementation)
|
||||
|
||||
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
|
||||
|
||||
virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
|
||||
|
||||
virtual int canRest() = 0;
|
||||
|
|
|
@ -624,6 +624,8 @@ namespace MWClass
|
|||
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);
|
||||
|
||||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
if(!successful)
|
||||
{
|
||||
// TODO: Handle HitAttemptOnMe script function
|
||||
|
@ -659,7 +661,6 @@ namespace MWClass
|
|||
{
|
||||
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();
|
||||
|
|
|
@ -170,6 +170,14 @@ namespace MWClass
|
|||
virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const;
|
||||
|
||||
virtual bool canSwim (const MWWorld::Ptr &ptr) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool canWalk (const MWWorld::Ptr &ptr) const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -609,7 +609,7 @@ namespace MWDialogue
|
|||
return 1; // known topics
|
||||
}
|
||||
|
||||
void DialogueManager::write (ESM::ESMWriter& writer) const
|
||||
void DialogueManager::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
{
|
||||
ESM::DialogueState state;
|
||||
|
||||
|
@ -621,6 +621,7 @@ namespace MWDialogue
|
|||
writer.startRecord (ESM::REC_DIAS);
|
||||
state.save (writer);
|
||||
writer.endRecord (ESM::REC_DIAS);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
|
||||
void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type)
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace MWDialogue
|
|||
|
||||
virtual int countSavedGameRecords() const;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) const;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
};
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace MWDialogue
|
|||
return count;
|
||||
}
|
||||
|
||||
void Journal::write (ESM::ESMWriter& writer) const
|
||||
void Journal::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
{
|
||||
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
|
||||
{
|
||||
|
@ -178,6 +178,7 @@ namespace MWDialogue
|
|||
writer.startRecord (ESM::REC_QUES);
|
||||
state.save (writer);
|
||||
writer.endRecord (ESM::REC_QUES);
|
||||
progress.increaseProgress();
|
||||
|
||||
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
|
||||
{
|
||||
|
@ -188,6 +189,7 @@ namespace MWDialogue
|
|||
writer.startRecord (ESM::REC_JOUR);
|
||||
entry.save (writer);
|
||||
writer.endRecord (ESM::REC_JOUR);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,6 +201,7 @@ namespace MWDialogue
|
|||
writer.startRecord (ESM::REC_JOUR);
|
||||
entry.save (writer);
|
||||
writer.endRecord (ESM::REC_JOUR);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
|
||||
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
|
||||
|
@ -214,6 +217,7 @@ namespace MWDialogue
|
|||
writer.startRecord (ESM::REC_JOUR);
|
||||
entry.save (writer);
|
||||
writer.endRecord (ESM::REC_JOUR);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace MWDialogue
|
|||
|
||||
virtual int countSavedGameRecords() const;
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer) const;
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
};
|
||||
|
|
|
@ -188,12 +188,13 @@ namespace MWGui
|
|||
break;
|
||||
|
||||
case GM_ClassCreate:
|
||||
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog);
|
||||
mCreateClassDialog = 0;
|
||||
mCreateClassDialog = new CreateClassDialog();
|
||||
if (!mCreateClassDialog)
|
||||
{
|
||||
mCreateClassDialog = new CreateClassDialog();
|
||||
mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
|
||||
mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
|
||||
}
|
||||
mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);
|
||||
mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
|
||||
mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
|
||||
mCreateClassDialog->setVisible(true);
|
||||
if (mCreationStage < CSE_RaceChosen)
|
||||
mCreationStage = CSE_RaceChosen;
|
||||
|
@ -531,8 +532,8 @@ namespace MWGui
|
|||
mPlayerClass = klass;
|
||||
MWBase::Environment::get().getWindowManager()->setPlayerClass(klass);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog);
|
||||
mCreateClassDialog = 0;
|
||||
// Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later
|
||||
mCreateClassDialog->setVisible(false);
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
|
@ -554,8 +555,8 @@ namespace MWGui
|
|||
|
||||
void CharacterCreation::onCreateClassDialogBack()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog);
|
||||
mCreateClassDialog = 0;
|
||||
// Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later
|
||||
mCreateClassDialog->setVisible(false);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace MWGui
|
|||
{
|
||||
}
|
||||
|
||||
void CompanionItemModel::copyItem (const ItemStack& item, size_t count)
|
||||
void CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
|
||||
{
|
||||
if (mActor.getClass().isNpc())
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ namespace MWGui
|
|||
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
|
||||
}
|
||||
|
||||
InventoryItemModel::copyItem(item, count);
|
||||
InventoryItemModel::copyItem(item, count, setNewOwner);
|
||||
}
|
||||
|
||||
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace MWGui
|
|||
public:
|
||||
CompanionItemModel (const MWWorld::Ptr& actor);
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count);
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
};
|
||||
|
||||
|
|
|
@ -215,16 +215,22 @@ namespace MWGui
|
|||
{
|
||||
std::vector<std::string> matches;
|
||||
listNames();
|
||||
mCommandLine->setCaption(complete( mCommandLine->getOnlyText(), matches ));
|
||||
#if 0
|
||||
int i = 0;
|
||||
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); ++it,++i )
|
||||
std::string oldCaption = mCommandLine->getCaption();
|
||||
std::string newCaption = complete( mCommandLine->getOnlyText(), matches );
|
||||
mCommandLine->setCaption(newCaption);
|
||||
|
||||
// List candidates if repeatedly pressing tab
|
||||
if (oldCaption == newCaption && matches.size())
|
||||
{
|
||||
printOK( *it );
|
||||
if( i == 50 )
|
||||
break;
|
||||
int i = 0;
|
||||
printOK("");
|
||||
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); ++it,++i )
|
||||
{
|
||||
printOK( *it );
|
||||
if( i == 50 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(mCommandHistory.empty()) return;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../mwworld/containerstore.hpp"
|
||||
|
||||
#include "../mwmechanics/pickpocket.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "countdialog.hpp"
|
||||
#include "tradewindow.hpp"
|
||||
|
@ -84,8 +85,7 @@ namespace MWGui
|
|||
// otherwise, do the transfer
|
||||
if (targetModel != mSourceModel)
|
||||
{
|
||||
targetModel->copyItem(mItem, mDraggedCount);
|
||||
mSourceModel->removeItem(mItem, mDraggedCount);
|
||||
mSourceModel->moveItem(mItem, mDraggedCount, targetModel);
|
||||
}
|
||||
|
||||
mSourceModel->update();
|
||||
|
@ -292,8 +292,7 @@ namespace MWGui
|
|||
if (!onTakeItem(item, item.mCount))
|
||||
break;
|
||||
|
||||
playerModel->copyItem(item, item.mCount);
|
||||
mModel->removeItem(item, item.mCount);
|
||||
mModel->moveItem(item, item.mCount, playerModel);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||
|
@ -341,7 +340,11 @@ namespace MWGui
|
|||
}
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count);
|
||||
// Looting a dead corpse is considered OK
|
||||
if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead())
|
||||
return true;
|
||||
else
|
||||
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void ContainerItemModel::copyItem (const ItemStack& item, size_t count)
|
||||
void ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
|
||||
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWGui
|
|||
virtual ModelIndex getIndex (ItemStack item);
|
||||
virtual size_t getItemCount();
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count);
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
|
||||
virtual void update();
|
||||
|
|
|
@ -196,6 +196,16 @@ namespace MWGui
|
|||
bitmapFile->read(&textureData[0], width*height*4);
|
||||
bitmapFile->close();
|
||||
|
||||
std::string resourceName;
|
||||
if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic"))
|
||||
resourceName = "Magic Cards";
|
||||
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century"))
|
||||
resourceName = "Century Gothic";
|
||||
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric"))
|
||||
resourceName = "Daedric";
|
||||
else
|
||||
return; // no point in loading it, since there is no way of using additional fonts
|
||||
|
||||
std::string textureName = name;
|
||||
Ogre::Image image;
|
||||
image.loadDynamicImage(&textureData[0], width, height, Ogre::PF_BYTE_RGBA);
|
||||
|
@ -208,18 +218,11 @@ namespace MWGui
|
|||
// Register the font with MyGUI
|
||||
MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>(
|
||||
MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont"));
|
||||
|
||||
// We need to emulate loading from XML because the data members are private as of mygui 3.2.0
|
||||
MyGUI::xml::Document xmlDocument;
|
||||
MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont");
|
||||
|
||||
if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic"))
|
||||
root->addAttribute("name", "Magic Cards");
|
||||
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century"))
|
||||
root->addAttribute("name", "Century Gothic");
|
||||
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric"))
|
||||
root->addAttribute("name", "Daedric");
|
||||
else
|
||||
return; // no point in loading it, since there is no way of using additional fonts
|
||||
root->addAttribute("name", resourceName);
|
||||
|
||||
MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property");
|
||||
defaultHeight->addAttribute("key", "DefaultHeight");
|
||||
|
@ -285,6 +288,7 @@ namespace MWGui
|
|||
|
||||
font->deserialization(root, MyGUI::Version(3,2,0));
|
||||
|
||||
MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName());
|
||||
MyGUI::ResourceManager::getInstance().addResource(font);
|
||||
}
|
||||
|
||||
|
|
|
@ -203,9 +203,9 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void HUD::setDrowningTimeLeft(float time)
|
||||
void HUD::setDrowningTimeLeft(float time, float maxTime)
|
||||
{
|
||||
size_t progress = time/20.0*200.0;
|
||||
size_t progress = time/maxTime*200.0;
|
||||
mDrowning->setProgressPosition(progress);
|
||||
|
||||
bool isDrowning = (progress == 0);
|
||||
|
@ -625,7 +625,7 @@ namespace MWGui
|
|||
if (mIsDrowning)
|
||||
{
|
||||
float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f;
|
||||
mDrowningFlash->setColour(MyGUI::Colour(intensity, intensity, intensity));
|
||||
mDrowningFlash->setColour(MyGUI::Colour(intensity, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,9 @@ namespace MWGui
|
|||
void setBatchCount(unsigned int count);
|
||||
|
||||
/// Set time left for the player to start drowning
|
||||
/// @param time value from [0,20]
|
||||
void setDrowningTimeLeft(float time);
|
||||
/// @param time time left to start drowning
|
||||
/// @param maxTime how long we can be underwater (in total) until drowning starts
|
||||
void setDrowningTimeLeft(float time, float maxTime);
|
||||
void setDrowningBarVisible(bool visible);
|
||||
|
||||
void setHmsVisible(bool visible);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
@ -38,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void InventoryItemModel::copyItem (const ItemStack& item, size_t count)
|
||||
void InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
|
||||
throw std::runtime_error("Item to copy needs to be from a different container!");
|
||||
mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor);
|
||||
mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,6 +59,18 @@ void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
|
|||
throw std::runtime_error("Not enough items in the stack to remove");
|
||||
}
|
||||
|
||||
void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
{
|
||||
bool setNewOwner = false;
|
||||
|
||||
// Are you dead? Then you wont need that anymore
|
||||
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead())
|
||||
setNewOwner = true;
|
||||
|
||||
otherModel->copyItem(item, count, setNewOwner);
|
||||
removeItem(item, count);
|
||||
}
|
||||
|
||||
void InventoryItemModel::update()
|
||||
{
|
||||
MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor);
|
||||
|
|
|
@ -15,9 +15,12 @@ namespace MWGui
|
|||
virtual ModelIndex getIndex (ItemStack item);
|
||||
virtual size_t getItemCount();
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count);
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
|
||||
/// Move items from this model to \a otherModel.
|
||||
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
|
||||
virtual void update();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace MWGui
|
|||
, mTrading(false)
|
||||
, mLastXSize(0)
|
||||
, mLastYSize(0)
|
||||
, mPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr())
|
||||
, mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()))
|
||||
, mPreviewDirty(true)
|
||||
, mDragAndDrop(dragAndDrop)
|
||||
, mSelectedItem(-1)
|
||||
|
@ -91,8 +91,8 @@ namespace MWGui
|
|||
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
|
||||
mSortModel = new SortFilterItemModel(mTradeModel);
|
||||
mItemView->setModel(mSortModel);
|
||||
mPreview = MWRender::InventoryPreview(mPtr);
|
||||
mPreview.setup();
|
||||
mPreview.reset(new MWRender::InventoryPreview(mPtr));
|
||||
mPreview->setup();
|
||||
}
|
||||
|
||||
void InventoryWindow::setGuiMode(GuiMode mode)
|
||||
|
@ -444,7 +444,7 @@ namespace MWGui
|
|||
|
||||
MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y)
|
||||
{
|
||||
int slot = mPreview.getSlotSelected (x, y);
|
||||
int slot = mPreview->getSlotSelected (x, y);
|
||||
|
||||
if (slot == -1)
|
||||
return MWWorld::Ptr();
|
||||
|
@ -493,7 +493,7 @@ namespace MWGui
|
|||
mPreviewDirty = false;
|
||||
MyGUI::IntSize size = mAvatarImage->getSize();
|
||||
|
||||
mPreview.update (size.width, size.height);
|
||||
mPreview->update (size.width, size.height);
|
||||
|
||||
mAvatarImage->setImageTexture("CharacterPreview");
|
||||
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height)));
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace MWGui
|
|||
MWWorld::Ptr getAvatarSelectedItem(int x, int y);
|
||||
|
||||
void rebuildAvatar() {
|
||||
mPreview.rebuild();
|
||||
mPreview->rebuild();
|
||||
}
|
||||
|
||||
TradeItemModel* getTradeModel();
|
||||
|
@ -81,7 +81,7 @@ namespace MWGui
|
|||
int mLastXSize;
|
||||
int mLastYSize;
|
||||
|
||||
MWRender::InventoryPreview mPreview;
|
||||
std::auto_ptr<MWRender::InventoryPreview> mPreview;
|
||||
|
||||
bool mTrading;
|
||||
|
||||
|
|
|
@ -71,16 +71,22 @@ namespace MWGui
|
|||
{
|
||||
}
|
||||
|
||||
void ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
{
|
||||
otherModel->copyItem(item, count);
|
||||
removeItem(item, count);
|
||||
}
|
||||
|
||||
|
||||
ProxyItemModel::~ProxyItemModel()
|
||||
{
|
||||
delete mSourceModel;
|
||||
}
|
||||
|
||||
void ProxyItemModel::copyItem (const ItemStack& item, size_t count)
|
||||
void ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
// no need to use mapToSource since itemIndex refers to an index in the sourceModel
|
||||
mSourceModel->copyItem (item, count);
|
||||
mSourceModel->copyItem (item, count, setNewOwner);
|
||||
}
|
||||
|
||||
void ProxyItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
|
|
|
@ -55,7 +55,11 @@ namespace MWGui
|
|||
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count) = 0;
|
||||
/// Move items from this model to \a otherModel.
|
||||
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
|
||||
/// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner?
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
|
||||
virtual void removeItem (const ItemStack& item, size_t count) = 0;
|
||||
|
||||
private:
|
||||
|
@ -69,7 +73,7 @@ namespace MWGui
|
|||
{
|
||||
public:
|
||||
virtual ~ProxyItemModel();
|
||||
virtual void copyItem (const ItemStack& item, size_t count);
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
virtual ModelIndex getIndex (ItemStack item);
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace MWGui
|
|||
|
||||
void MWList::redraw(bool scrollbarShown)
|
||||
{
|
||||
const int _scrollBarWidth = 24; // fetch this from skin?
|
||||
const int _scrollBarWidth = 20; // fetch this from skin?
|
||||
const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;
|
||||
const int spacing = 3;
|
||||
size_t scrollbarPosition = mScrollView->getScrollPosition();
|
||||
|
@ -83,7 +83,7 @@ namespace MWGui
|
|||
else
|
||||
{
|
||||
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine",
|
||||
MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18),
|
||||
MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth() - scrollBarWidth - 4, 18),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);
|
||||
separator->setNeedMouseFocus(false);
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace MWGui
|
|||
}
|
||||
++i;
|
||||
}
|
||||
mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height));
|
||||
mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height));
|
||||
|
||||
if (!scrollbarShown && mItemHeight > mClient->getSize().height)
|
||||
redraw(true);
|
||||
|
|
|
@ -160,7 +160,6 @@ namespace MWGui
|
|||
|
||||
void LoadingScreen::setProgress (size_t value)
|
||||
{
|
||||
assert(value < mProgressBar->getScrollRange());
|
||||
if (value - mProgress < mProgressBar->getScrollRange()/100.f)
|
||||
return;
|
||||
mProgress = value;
|
||||
|
@ -174,7 +173,6 @@ namespace MWGui
|
|||
mProgressBar->setScrollPosition(0);
|
||||
size_t value = mProgress + increase;
|
||||
mProgress = value;
|
||||
assert(mProgress < mProgressBar->getScrollRange());
|
||||
mProgressBar->setTrackSize(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize());
|
||||
draw();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MWGui
|
|||
|
||||
virtual void setProgressRange (size_t range);
|
||||
virtual void setProgress (size_t value);
|
||||
virtual void increaseProgress (size_t increase);
|
||||
virtual void increaseProgress (size_t increase=1);
|
||||
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace MWGui
|
|||
{
|
||||
getWidget(mVersionText, "VersionText");
|
||||
std::stringstream sstream;
|
||||
sstream << "OpenMW version: " << OPENMW_VERSION;
|
||||
sstream << "OpenMW Version: " << OPENMW_VERSION;
|
||||
|
||||
// adding info about git hash if available
|
||||
std::string rev = OPENMW_VERSION_COMMITHASH;
|
||||
|
@ -36,7 +36,7 @@ namespace MWGui
|
|||
if (!rev.empty() && !tag.empty())
|
||||
{
|
||||
rev = rev.substr(0,10);
|
||||
sstream << "\nrevision: " << rev;
|
||||
sstream << "\nRevision: " << rev;
|
||||
}
|
||||
|
||||
std::string output = sstream.str();
|
||||
|
@ -170,7 +170,8 @@ namespace MWGui
|
|||
buttons.push_back("loadgame");
|
||||
|
||||
if (state==MWBase::StateManager::State_Running &&
|
||||
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1)
|
||||
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 &&
|
||||
MWBase::Environment::get().getWindowManager()->isSavingAllowed())
|
||||
buttons.push_back("savegame");
|
||||
|
||||
buttons.push_back("options");
|
||||
|
|
|
@ -595,7 +595,7 @@ namespace MWGui
|
|||
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
|
||||
}
|
||||
|
||||
void MapWindow::write(ESM::ESMWriter &writer)
|
||||
void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress)
|
||||
{
|
||||
ESM::GlobalMap map;
|
||||
mGlobalMapRender->write(map);
|
||||
|
@ -605,6 +605,7 @@ namespace MWGui
|
|||
writer.startRecord(ESM::REC_GMAP);
|
||||
map.save(writer);
|
||||
writer.endRecord(ESM::REC_GMAP);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
|
||||
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace MWGui
|
|||
/// Clear all savegame-specific data
|
||||
void clear();
|
||||
|
||||
void write (ESM::ESMWriter& writer);
|
||||
void write (ESM::ESMWriter& writer, Loading::Listener& progress);
|
||||
void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/esm/quickkeys.hpp>
|
||||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
|
@ -55,6 +57,14 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void QuickKeysMenu::clear()
|
||||
{
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
unassign(mQuickKeyButtons[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
QuickKeysMenu::~QuickKeysMenu()
|
||||
{
|
||||
delete mAssignDialog;
|
||||
|
@ -154,8 +164,6 @@ namespace MWGui
|
|||
frame->setUserString ("ToolTipType", "ItemPtr");
|
||||
frame->setUserData(item);
|
||||
frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
|
||||
|
||||
|
||||
MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
|
||||
std::string path = std::string("icons\\");
|
||||
path += MWWorld::Class::get(item).getInventoryIcon(item);
|
||||
|
@ -165,7 +173,8 @@ namespace MWGui
|
|||
image->setImageTexture (path);
|
||||
image->setNeedMouseFocus (false);
|
||||
|
||||
mItemSelectionDialog->setVisible(false);
|
||||
if (mItemSelectionDialog)
|
||||
mItemSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignItemCancel()
|
||||
|
@ -198,7 +207,8 @@ namespace MWGui
|
|||
image->setImageTexture (path);
|
||||
image->setNeedMouseFocus (false);
|
||||
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
if (mMagicSelectionDialog)
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
|
||||
|
@ -239,7 +249,8 @@ namespace MWGui
|
|||
image->setImageTexture (path);
|
||||
image->setNeedMouseFocus (false);
|
||||
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
if (mMagicSelectionDialog)
|
||||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignMagicCancel ()
|
||||
|
@ -374,6 +385,110 @@ namespace MWGui
|
|||
center();
|
||||
}
|
||||
|
||||
void QuickKeysMenu::write(ESM::ESMWriter &writer)
|
||||
{
|
||||
writer.startRecord(ESM::REC_KEYS);
|
||||
|
||||
ESM::QuickKeys keys;
|
||||
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
MyGUI::Button* button = mQuickKeyButtons[i];
|
||||
|
||||
int type = *button->getUserData<QuickKeyType>();
|
||||
|
||||
ESM::QuickKeys::QuickKey key;
|
||||
key.mType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Type_Unassigned:
|
||||
break;
|
||||
case Type_Item:
|
||||
case Type_MagicItem:
|
||||
{
|
||||
MWWorld::Ptr item = *button->getChildAt(0)->getUserData<MWWorld::Ptr>();
|
||||
key.mId = item.getCellRef().mRefID;
|
||||
break;
|
||||
}
|
||||
case Type_Magic:
|
||||
std::string spellId = button->getChildAt(0)->getUserString("Spell");
|
||||
key.mId = spellId;
|
||||
break;
|
||||
}
|
||||
|
||||
keys.mKeys.push_back(key);
|
||||
}
|
||||
|
||||
keys.save(writer);
|
||||
|
||||
writer.endRecord(ESM::REC_KEYS);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::readRecord(ESM::ESMReader &reader, int32_t type)
|
||||
{
|
||||
if (type != ESM::REC_KEYS)
|
||||
return;
|
||||
|
||||
ESM::QuickKeys keys;
|
||||
keys.load(reader);
|
||||
|
||||
int i=0;
|
||||
for (std::vector<ESM::QuickKeys::QuickKey>::const_iterator it = keys.mKeys.begin(); it != keys.mKeys.end(); ++it)
|
||||
{
|
||||
if (i >= 10)
|
||||
return;
|
||||
|
||||
mSelectedIndex = i;
|
||||
int keyType = it->mType;
|
||||
std::string id = it->mId;
|
||||
MyGUI::Button* button = mQuickKeyButtons[i];
|
||||
|
||||
switch (keyType)
|
||||
{
|
||||
case Type_Magic:
|
||||
onAssignMagic(id);
|
||||
break;
|
||||
case Type_Item:
|
||||
case Type_MagicItem:
|
||||
{
|
||||
// Find the item by id
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
MWWorld::Ptr item;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id))
|
||||
{
|
||||
if (item.isEmpty() ||
|
||||
// Prefer the stack with the lowest remaining uses
|
||||
(it->getCellRef().mCharge != -1 && (item.getCellRef().mCharge == -1 || it->getCellRef().mCharge < item.getCellRef().mCharge) ))
|
||||
{
|
||||
item = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.isEmpty())
|
||||
unassign(button, i);
|
||||
else
|
||||
{
|
||||
if (keyType == Type_Item)
|
||||
onAssignItem(item);
|
||||
else if (keyType == Type_MagicItem)
|
||||
onAssignMagicItem(item);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Type_Unassigned:
|
||||
unassign(button, i);
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@ namespace MWGui
|
|||
};
|
||||
|
||||
|
||||
void write (ESM::ESMWriter& writer);
|
||||
void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
void clear();
|
||||
|
||||
|
||||
private:
|
||||
MyGUI::EditBox* mInstructionLabel;
|
||||
MyGUI::Button* mOkButton;
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace MWGui
|
|||
|
||||
void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable
|
||||
|
||||
void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; }
|
||||
|
||||
protected:
|
||||
virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace MWGui
|
|||
: WindowModal("openmw_savegame_dialog.layout")
|
||||
, mSaving(true)
|
||||
, mCurrentCharacter(NULL)
|
||||
, mCurrentSlot(NULL)
|
||||
{
|
||||
getWidget(mScreenshot, "Screenshot");
|
||||
getWidget(mCharacterSelection, "SelectCharacter");
|
||||
|
@ -36,6 +37,7 @@ namespace MWGui
|
|||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
|
||||
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
|
||||
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
|
||||
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
|
||||
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
|
||||
mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);
|
||||
mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);
|
||||
|
@ -47,6 +49,37 @@ namespace MWGui
|
|||
accept();
|
||||
}
|
||||
|
||||
void SaveGameDialog::onSlotMouseClick(MyGUI::ListBox* sender, size_t pos)
|
||||
{
|
||||
onSlotSelected(sender, pos);
|
||||
|
||||
if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed())
|
||||
{
|
||||
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
|
||||
dialog->open("#{sMessage3}");
|
||||
dialog->eventOkClicked.clear();
|
||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed);
|
||||
dialog->eventCancelClicked.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveGameDialog::onDeleteSlotConfirmed()
|
||||
{
|
||||
MWBase::Environment::get().getStateManager()->deleteGame (mCurrentCharacter, mCurrentSlot);
|
||||
mSaveList->removeItemAt(mSaveList->getIndexSelected());
|
||||
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
|
||||
|
||||
// The character might be deleted now
|
||||
size_t previousIndex = mCharacterSelection->getIndexSelected();
|
||||
open();
|
||||
if (mCharacterSelection->getItemCount())
|
||||
{
|
||||
size_t nextCharacter = std::min(previousIndex, mCharacterSelection->getItemCount()-1);
|
||||
mCharacterSelection->setIndexSelected(nextCharacter);
|
||||
onCharacterSelected(mCharacterSelection, nextCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender)
|
||||
{
|
||||
// This might have previously been a save slot from the list. If so, that is no longer the case
|
||||
|
@ -69,6 +102,12 @@ namespace MWGui
|
|||
|
||||
center();
|
||||
|
||||
mCharacterSelection->setCaption("");
|
||||
mCharacterSelection->removeAllItems();
|
||||
mCurrentCharacter = NULL;
|
||||
mCurrentSlot = NULL;
|
||||
mSaveList->removeAllItems();
|
||||
|
||||
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
|
||||
if (mgr->characterBegin() == mgr->characterEnd())
|
||||
return;
|
||||
|
@ -78,8 +117,6 @@ namespace MWGui
|
|||
std::string directory =
|
||||
Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves"));
|
||||
|
||||
mCharacterSelection->removeAllItems();
|
||||
|
||||
int selectedIndex = MyGUI::ITEM_NONE;
|
||||
|
||||
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)
|
||||
|
@ -152,23 +189,10 @@ namespace MWGui
|
|||
{
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
|
||||
|
||||
// Get the selected slot, if any
|
||||
unsigned int i=0;
|
||||
const MWState::Slot* slot = NULL;
|
||||
|
||||
if (mCurrentCharacter)
|
||||
{
|
||||
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i)
|
||||
{
|
||||
if (i == mSaveList->getIndexSelected())
|
||||
slot = &*it;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSaving)
|
||||
{
|
||||
// If overwriting an existing slot, ask for confirmation first
|
||||
if (slot != NULL && !reallySure)
|
||||
if (mCurrentSlot != NULL && !reallySure)
|
||||
{
|
||||
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
|
||||
dialog->open("#{sMessage4}");
|
||||
|
@ -182,18 +206,22 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}");
|
||||
return;
|
||||
}
|
||||
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCurrentCharacter && slot)
|
||||
{
|
||||
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);
|
||||
|
||||
if (mSaving)
|
||||
{
|
||||
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), mCurrentSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCurrentCharacter && mCurrentSlot)
|
||||
{
|
||||
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot);
|
||||
}
|
||||
}
|
||||
|
||||
if (MWBase::Environment::get().getStateManager()->getState()==
|
||||
MWBase::StateManager::State_NoGame)
|
||||
|
@ -221,6 +249,7 @@ namespace MWGui
|
|||
assert(character && "Can't find selected character");
|
||||
|
||||
mCurrentCharacter = character;
|
||||
mCurrentSlot = NULL;
|
||||
fillSaveList();
|
||||
}
|
||||
|
||||
|
@ -240,6 +269,7 @@ namespace MWGui
|
|||
{
|
||||
if (pos == MyGUI::ITEM_NONE)
|
||||
{
|
||||
mCurrentSlot = NULL;
|
||||
mInfoText->setCaption("");
|
||||
mScreenshot->setImageTexture("");
|
||||
return;
|
||||
|
@ -248,17 +278,17 @@ namespace MWGui
|
|||
if (mSaving)
|
||||
mSaveNameEdit->setCaption(sender->getItemNameAt(pos));
|
||||
|
||||
const MWState::Slot* slot = NULL;
|
||||
mCurrentSlot = NULL;
|
||||
unsigned int i=0;
|
||||
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)
|
||||
{
|
||||
if (i == pos)
|
||||
slot = &*it;
|
||||
mCurrentSlot = &*it;
|
||||
}
|
||||
assert(slot && "Can't find selected slot");
|
||||
assert(mCurrentSlot && "Can't find selected slot");
|
||||
|
||||
std::stringstream text;
|
||||
time_t time = slot->mTimeStamp;
|
||||
time_t time = mCurrentSlot->mTimeStamp;
|
||||
struct tm* timeinfo;
|
||||
timeinfo = localtime(&time);
|
||||
|
||||
|
@ -269,24 +299,24 @@ namespace MWGui
|
|||
char buffer[size];
|
||||
if (std::strftime(buffer, size, "%x %X", timeinfo) > 0)
|
||||
text << buffer << "\n";
|
||||
text << "Level " << slot->mProfile.mPlayerLevel << "\n";
|
||||
text << slot->mProfile.mPlayerCell << "\n";
|
||||
text << "Level " << mCurrentSlot->mProfile.mPlayerLevel << "\n";
|
||||
text << mCurrentSlot->mProfile.mPlayerCell << "\n";
|
||||
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
|
||||
|
||||
int hour = int(slot->mProfile.mInGameTime.mGameHour);
|
||||
int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour);
|
||||
bool pm = hour >= 12;
|
||||
if (hour >= 13) hour -= 12;
|
||||
if (hour == 0) hour = 12;
|
||||
|
||||
text
|
||||
<< slot->mProfile.mInGameTime.mDay << " "
|
||||
<< MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth)
|
||||
<< mCurrentSlot->mProfile.mInGameTime.mDay << " "
|
||||
<< MWBase::Environment::get().getWorld()->getMonthName(mCurrentSlot->mProfile.mInGameTime.mMonth)
|
||||
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
|
||||
|
||||
mInfoText->setCaptionWithReplacing(text.str());
|
||||
|
||||
// Decode screenshot
|
||||
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
|
||||
std::vector<char> data = mCurrentSlot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
|
||||
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
|
||||
Ogre::Image image;
|
||||
image.load(stream, "jpg");
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
namespace MWState
|
||||
{
|
||||
class Character;
|
||||
class Slot;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
|
@ -24,8 +25,15 @@ namespace MWGui
|
|||
void onCancelButtonClicked (MyGUI::Widget* sender);
|
||||
void onOkButtonClicked (MyGUI::Widget* sender);
|
||||
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
|
||||
// Slot selected (mouse click or arrow keys)
|
||||
void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
|
||||
// Slot activated (double click or enter key)
|
||||
void onSlotActivated (MyGUI::ListBox* sender, size_t pos);
|
||||
// Slot clicked with mouse
|
||||
void onSlotMouseClick(MyGUI::ListBox* sender, size_t pos);
|
||||
|
||||
void onDeleteSlotConfirmed();
|
||||
|
||||
void onEditSelectAccept (MyGUI::EditBox* sender);
|
||||
void onSaveNameChanged (MyGUI::EditBox* sender);
|
||||
void onConfirmationGiven();
|
||||
|
@ -46,6 +54,7 @@ namespace MWGui
|
|||
MyGUI::Widget* mSpacer;
|
||||
|
||||
const MWState::Character* mCurrentCharacter;
|
||||
const MWState::Slot* mCurrentSlot;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -475,7 +475,7 @@ namespace MWGui
|
|||
text += std::string("#DDC79E") + faction->mName;
|
||||
|
||||
if (expelled.find(it->first) != expelled.end())
|
||||
text += "\n#{sExpelled}";
|
||||
text += "\n#BF9959#{sExpelled}";
|
||||
else
|
||||
{
|
||||
text += std::string("\n#BF9959") + faction->mRanks[it->second];
|
||||
|
|
|
@ -120,14 +120,11 @@ namespace MWGui
|
|||
if (i == sourceModel->getItemCount())
|
||||
throw std::runtime_error("The borrowed item disappeared");
|
||||
|
||||
// reset owner before copying
|
||||
// reset owner while copying, but only for items bought by the player
|
||||
bool setNewOwner = (mMerchant.isEmpty());
|
||||
const ItemStack& item = sourceModel->getItem(i);
|
||||
std::string owner = item.mBase.getCellRef().mOwner;
|
||||
if (mMerchant.isEmpty()) // only for items bought by player
|
||||
item.mBase.getCellRef().mOwner = "";
|
||||
// copy the borrowed items to our model
|
||||
copyItem(item, it->mCount);
|
||||
item.mBase.getCellRef().mOwner = owner;
|
||||
copyItem(item, it->mCount, setNewOwner);
|
||||
// then remove them from the source model
|
||||
sourceModel->removeItem(item, it->mCount);
|
||||
}
|
||||
|
|
|
@ -78,13 +78,13 @@ namespace MWGui
|
|||
}
|
||||
|
||||
void TradeWindow::startTrade(const MWWorld::Ptr& actor)
|
||||
{
|
||||
{
|
||||
mPtr = actor;
|
||||
|
||||
mCurrentBalance = 0;
|
||||
mCurrentMerchantOffer = 0;
|
||||
|
||||
checkTradeTime();
|
||||
checkTradeTime();
|
||||
|
||||
std::vector<MWWorld::Ptr> itemSources;
|
||||
MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
|
||||
|
@ -245,7 +245,7 @@ namespace MWGui
|
|||
// were there any items traded at all?
|
||||
std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs();
|
||||
std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs();
|
||||
if (!playerBought.size() && !merchantBought.size())
|
||||
if (playerBought.empty() && merchantBought.empty())
|
||||
{
|
||||
// user notification
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
|
@ -476,7 +476,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
// Relates to NPC gold reset delay
|
||||
void TradeWindow::checkTradeTime()
|
||||
void TradeWindow::checkTradeTime()
|
||||
{
|
||||
MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
double delay = boost::lexical_cast<double>(MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fBarterGoldResetDelay")->getInt());
|
||||
|
@ -488,14 +488,14 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void TradeWindow::updateTradeTime()
|
||||
void TradeWindow::updateTradeTime()
|
||||
{
|
||||
MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr);
|
||||
MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
double delay = boost::lexical_cast<double>(MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fBarterGoldResetDelay")->getInt());
|
||||
|
||||
// If trade timestamp is within reset delay don't set
|
||||
if ( ! (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() &&
|
||||
if ( ! (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() &&
|
||||
MWBase::Environment::get().getWorld()->getTimeStamp() < sellerStats.getTradeTime() + delay) )
|
||||
{
|
||||
sellerStats.setTradeTime(MWBase::Environment::get().getWorld()->getTimeStamp());
|
||||
|
|
|
@ -625,9 +625,9 @@ namespace MWGui
|
|||
mStatsWindow->setValue (id, value);
|
||||
}
|
||||
|
||||
void WindowManager::setDrowningTimeLeft (float time)
|
||||
void WindowManager::setDrowningTimeLeft (float time, float maxTime)
|
||||
{
|
||||
mHud->setDrowningTimeLeft(time);
|
||||
mHud->setDrowningTimeLeft(time, maxTime);
|
||||
}
|
||||
|
||||
void WindowManager::setPlayerClass (const ESM::Class &class_)
|
||||
|
@ -1405,16 +1405,49 @@ namespace MWGui
|
|||
void WindowManager::clear()
|
||||
{
|
||||
mMap->clear();
|
||||
mQuickKeysMenu->clear();
|
||||
|
||||
mTrainingWindow->resetReference();
|
||||
mDialogueWindow->resetReference();
|
||||
mTradeWindow->resetReference();
|
||||
mSpellBuyingWindow->resetReference();
|
||||
mSpellCreationDialog->resetReference();
|
||||
mEnchantingDialog->resetReference();
|
||||
mContainerWindow->resetReference();
|
||||
mCompanionWindow->resetReference();
|
||||
mConsole->resetReference();
|
||||
|
||||
mGuiModes.clear();
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void WindowManager::write(ESM::ESMWriter &writer)
|
||||
void WindowManager::write(ESM::ESMWriter &writer, Loading::Listener& progress)
|
||||
{
|
||||
mMap->write(writer);
|
||||
mMap->write(writer, progress);
|
||||
|
||||
mQuickKeysMenu->write(writer);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
|
||||
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
|
||||
{
|
||||
mMap->readRecord(reader, type);
|
||||
if (type == ESM::REC_GMAP)
|
||||
mMap->readRecord(reader, type);
|
||||
else if (type == ESM::REC_KEYS)
|
||||
mQuickKeysMenu->readRecord(reader, type);
|
||||
}
|
||||
|
||||
int WindowManager::countSavedGameRecords() const
|
||||
{
|
||||
return 1 // Global map
|
||||
+ 1; // QuickKeysMenu
|
||||
}
|
||||
|
||||
bool WindowManager::isSavingAllowed() const
|
||||
{
|
||||
return !MyGUI::InputManager::getInstance().isModalAny()
|
||||
// TODO: remove this, once we have properly serialized the state of open windows
|
||||
&& (!isGuiMode() || (mGuiModes.size() == 1 && getMode() == GM_MainMenu));
|
||||
}
|
||||
|
||||
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
||||
|
|
|
@ -166,8 +166,9 @@ namespace MWGui
|
|||
virtual void setValue (const std::string& id, int value);
|
||||
|
||||
/// Set time left for the player to start drowning (update the drowning bar)
|
||||
/// @param time value from [0,20]
|
||||
virtual void setDrowningTimeLeft (float time);
|
||||
/// @param time time left to start drowning
|
||||
/// @param maxTime how long we can be underwater (in total) until drowning starts
|
||||
virtual void setDrowningTimeLeft (float time, float maxTime);
|
||||
|
||||
virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player
|
||||
virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group.
|
||||
|
@ -290,8 +291,12 @@ namespace MWGui
|
|||
/// Clear all savegame-specific data
|
||||
virtual void clear();
|
||||
|
||||
virtual void write (ESM::ESMWriter& writer);
|
||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress);
|
||||
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
virtual int countSavedGameRecords() const;
|
||||
|
||||
/// Does the current stack of GUI-windows permit saving?
|
||||
virtual bool isSavingAllowed() const;
|
||||
|
||||
private:
|
||||
bool mConsoleOnlyScripts;
|
||||
|
|
|
@ -206,7 +206,7 @@ namespace MWMechanics
|
|||
if (effectIt->mKey.mId == effectId)
|
||||
effectIt = it->second.mEffects.erase(effectIt);
|
||||
else
|
||||
effectIt++;
|
||||
++effectIt;
|
||||
}
|
||||
}
|
||||
mSpellsChanged = true;
|
||||
|
@ -224,7 +224,7 @@ namespace MWMechanics
|
|||
&& it->second.mCasterHandle == actorHandle)
|
||||
effectIt = it->second.mEffects.erase(effectIt);
|
||||
else
|
||||
effectIt++;
|
||||
++effectIt;
|
||||
}
|
||||
}
|
||||
mSpellsChanged = true;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include "aicombat.hpp"
|
||||
#include "aifollow.hpp"
|
||||
#include "aipersue.hpp"
|
||||
#include "aipursue.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -194,21 +194,11 @@ namespace MWMechanics
|
|||
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
|
||||
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
|
||||
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||
float disp = 100; //creatures don't have disposition, so set it to 100 by default
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
|
||||
}
|
||||
|
||||
if( (fight == 100 )
|
||||
|| (fight >= 95 && d <= 3000)
|
||||
|| (fight >= 90 && d <= 2000)
|
||||
|| (fight >= 80 && d <= 1000)
|
||||
|| (fight >= 80 && disp <= 40)
|
||||
|| (fight >= 70 && disp <= 35 && d <= 1000)
|
||||
|| (fight >= 60 && disp <= 30 && d <= 1000)
|
||||
|| (fight >= 50 && disp == 0)
|
||||
|| (fight >= 40 && disp <= 10 && d <= 500)
|
||||
)
|
||||
{
|
||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player)
|
||||
|
@ -216,7 +206,7 @@ namespace MWMechanics
|
|||
|
||||
if (LOS)
|
||||
{
|
||||
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()));
|
||||
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()), ptr);
|
||||
creatureStats.setHostile(true);
|
||||
}
|
||||
}
|
||||
|
@ -547,7 +537,7 @@ namespace MWMechanics
|
|||
|
||||
// TODO: Add AI to follow player and fight for him
|
||||
AiFollow package(ptr.getRefData().getHandle());
|
||||
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package);
|
||||
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr);
|
||||
// TODO: VFX_SummonStart, VFX_SummonEnd
|
||||
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
|
||||
|
@ -729,34 +719,33 @@ namespace MWMechanics
|
|||
CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
|
||||
NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr);
|
||||
|
||||
// If I'm a guard and I'm not hostile
|
||||
if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile())
|
||||
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
|
||||
{
|
||||
/// \todo Move me! I shouldn't be here...
|
||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt()) *
|
||||
float(esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->getInt()) *
|
||||
esmStore.get<ESM::GameSetting>().find("fCrimeGoldDiscountMult")->getFloat();
|
||||
// Attack on sight if bounty is greater than the cutoff
|
||||
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
||||
// Force dialogue on sight if bounty is greater than the cutoff
|
||||
// In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
|
||||
if ( player.getClass().getNpcStats(player).getBounty() >= cutoff
|
||||
// TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player)
|
||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
|
||||
{
|
||||
creatureStats.getAiSequence().stack(AiCombat(player));
|
||||
creatureStats.setHostile(true);
|
||||
npcStats.setCrimeId( MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() );
|
||||
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);
|
||||
creatureStats.setAlarmed(true);
|
||||
npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
|
||||
}
|
||||
}
|
||||
|
||||
// if I was a witness to a crime
|
||||
if (npcStats.getCrimeId() != -1)
|
||||
{
|
||||
// if you've payed for your crimes and I havent noticed
|
||||
// if you've paid for your crimes and I havent noticed
|
||||
if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() )
|
||||
{
|
||||
// Calm witness down
|
||||
if (ptr.getClass().isClass(ptr, "Guard"))
|
||||
creatureStats.getAiSequence().stopPersue();
|
||||
creatureStats.getAiSequence().stopPursuit();
|
||||
creatureStats.getAiSequence().stopCombat();
|
||||
|
||||
// Reset factors to attack
|
||||
|
@ -771,17 +760,16 @@ namespace MWMechanics
|
|||
else if (!creatureStats.isHostile())
|
||||
{
|
||||
if (ptr.getClass().isClass(ptr, "Guard"))
|
||||
creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player)));
|
||||
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);
|
||||
else
|
||||
creatureStats.getAiSequence().stack(AiCombat(player));
|
||||
creatureStats.getAiSequence().stack(AiCombat(player), ptr);
|
||||
creatureStats.setHostile(true);
|
||||
}
|
||||
}
|
||||
|
||||
// if I didn't report a crime was I attacked?
|
||||
else if (creatureStats.getAttacked() && !creatureStats.isHostile())
|
||||
{
|
||||
creatureStats.getAiSequence().stack(AiCombat(player));
|
||||
creatureStats.getAiSequence().stack(AiCombat(player), ptr);
|
||||
creatureStats.setHostile(true);
|
||||
}
|
||||
}
|
||||
|
@ -889,12 +877,23 @@ namespace MWMechanics
|
|||
iter->second->update(duration);
|
||||
}
|
||||
|
||||
// Kill dead actors
|
||||
// Kill dead actors, update some variables
|
||||
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
|
||||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
|
||||
CreatureStats &stats = cls.getCreatureStats(iter->first);
|
||||
|
||||
//KnockedOutOneFrameLogic
|
||||
//Used for "OnKnockedOut" command
|
||||
//Put here to ensure that it's run for PRECISELY one frame.
|
||||
if(stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Start it for one frame if nessesary
|
||||
stats.setKnockedDownOneFrame(true);
|
||||
}
|
||||
else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Turn off KnockedOutOneframe
|
||||
stats.setKnockedDownOneFrame(false);
|
||||
stats.setKnockedDownOverOneFrame(true);
|
||||
}
|
||||
|
||||
if(!stats.isDead())
|
||||
{
|
||||
if(iter->second->isDead())
|
||||
|
@ -1028,8 +1027,7 @@ namespace MWMechanics
|
|||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
|
||||
CreatureStats &stats = cls.getCreatureStats(iter->first);
|
||||
|
||||
if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow)
|
||||
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow)
|
||||
{
|
||||
MWMechanics::AiFollow* package = static_cast<MWMechanics::AiFollow*>(stats.getAiSequence().getActivePackage());
|
||||
if(package->getFollowedActor() == actor.getCellRef().mRefID)
|
||||
|
@ -1050,8 +1048,7 @@ namespace MWMechanics
|
|||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(*iter);
|
||||
CreatureStats &stats = cls.getCreatureStats(*iter);
|
||||
|
||||
if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
|
||||
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
|
||||
{
|
||||
MWMechanics::AiCombat* package = static_cast<MWMechanics::AiCombat*>(stats.getAiSequence().getActivePackage());
|
||||
if(package->getTargetId() == actor.getCellRef().mRefID)
|
||||
|
|
|
@ -31,10 +31,52 @@ namespace
|
|||
|
||||
//chooses an attack depending on probability to avoid uniformity
|
||||
void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
|
||||
|
||||
float getZAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
|
||||
{
|
||||
float len = (dirLen > 0.0f)? dirLen : dir.length();
|
||||
return Ogre::Radian( Ogre::Math::ACos(dir.y / len) * sgn(Ogre::Math::ASin(dir.x / len)) ).valueDegrees();
|
||||
}
|
||||
|
||||
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
|
||||
{
|
||||
float len = (dirLen > 0.0f)? dirLen : dir.length();
|
||||
return Ogre::Radian(-Ogre::Math::ASin(dir.z / len)).valueDegrees();
|
||||
}
|
||||
|
||||
|
||||
const float PATHFIND_Z_REACH = 50.0f;
|
||||
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
||||
const float PATHFIND_CAUTION_DIST = 500.0f;
|
||||
// distance after which actor (failed previously to shortcut) will try again
|
||||
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
|
||||
|
||||
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
|
||||
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
|
||||
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offset)
|
||||
{
|
||||
if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH)
|
||||
{
|
||||
Ogre::Vector3 dir = to - from;
|
||||
dir.z = 0;
|
||||
dir.normalise();
|
||||
float verticalOffset = 200; // instead of '200' here we want the height of the actor
|
||||
Ogre::Vector3 _from = from + dir*offset + Ogre::Vector3::UNIT_Z * verticalOffset;
|
||||
|
||||
// cast up-down ray and find height in world space of hit
|
||||
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
|
||||
|
||||
if(abs(from.z - h) <= PATHFIND_Z_REACH)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
static const float MAX_ATTACK_DURATION = 0.35f;
|
||||
static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander
|
||||
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
|
||||
|
||||
|
@ -45,16 +87,16 @@ namespace MWMechanics
|
|||
mTimerCombatMove(0),
|
||||
mFollowTarget(false),
|
||||
mReadyToAttack(false),
|
||||
mStrike(false),
|
||||
mAttack(false),
|
||||
mCombatMove(false),
|
||||
mBackOffDoor(false),
|
||||
mRotate(false),
|
||||
mMovement(),
|
||||
mForceNoShortcut(false),
|
||||
mShortcutFailPos(),
|
||||
mBackOffDoor(false),
|
||||
mCell(NULL),
|
||||
mDoorIter(actor.getCell()->get<ESM::Door>().mList.end()),
|
||||
mDoors(actor.getCell()->get<ESM::Door>()),
|
||||
mDoorCheckDuration(0),
|
||||
mTargetAngle(0)
|
||||
mDoorCheckDuration(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -125,17 +167,23 @@ namespace MWMechanics
|
|||
mCombatMove = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
actor.getClass().getMovementSettings(actor) = mMovement;
|
||||
actor.getClass().getMovementSettings(actor).mRotation[0] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mRotation[2] = 0;
|
||||
|
||||
if (mRotate)
|
||||
if(mMovement.mRotation[2] != 0)
|
||||
{
|
||||
if (zTurn(actor, Ogre::Degree(mTargetAngle)))
|
||||
mRotate = false;
|
||||
if(zTurn(actor, Ogre::Degree(mMovement.mRotation[2]))) mMovement.mRotation[2] = 0;
|
||||
}
|
||||
|
||||
if(mMovement.mRotation[0] != 0)
|
||||
{
|
||||
if(smoothTurn(actor, Ogre::Degree(mMovement.mRotation[0]), 0)) mMovement.mRotation[0] = 0;
|
||||
}
|
||||
|
||||
mTimerAttack -= duration;
|
||||
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
|
||||
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mAttack);
|
||||
|
||||
float tReaction = 0.25f;
|
||||
if(mTimerReact < tReaction)
|
||||
|
@ -145,7 +193,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
//Update with period = tReaction
|
||||
|
||||
|
||||
mTimerReact = 0;
|
||||
|
||||
bool cellChange = mCell && (actor.getCell() != mCell);
|
||||
|
@ -156,16 +204,16 @@ namespace MWMechanics
|
|||
|
||||
//actual attacking logic
|
||||
//TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f
|
||||
float attackPeriod = 1.0f;
|
||||
float attacksPeriod = 1.0f;
|
||||
if(mReadyToAttack)
|
||||
{
|
||||
if(mTimerAttack <= -attackPeriod)
|
||||
if(mTimerAttack <= -attacksPeriod)
|
||||
{
|
||||
//TODO: should depend on time between 'start' to 'min attack'
|
||||
//for better controlling of NPCs' attack strength.
|
||||
//Also it seems that this time is different for slash/thrust/chop
|
||||
mTimerAttack = 0.35f * static_cast<float>(rand())/RAND_MAX;
|
||||
mStrike = true;
|
||||
mTimerAttack = MAX_ATTACK_DURATION * static_cast<float>(rand())/RAND_MAX;
|
||||
mAttack = true;
|
||||
|
||||
//say a provoking combat phrase
|
||||
if (actor.getClass().isNpc())
|
||||
|
@ -180,30 +228,31 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
else if (mTimerAttack <= 0)
|
||||
mStrike = false;
|
||||
mAttack = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTimerAttack = -attackPeriod;
|
||||
mStrike = false;
|
||||
mTimerAttack = -attacksPeriod;
|
||||
mAttack = false;
|
||||
}
|
||||
|
||||
const MWWorld::Class &cls = actor.getClass();
|
||||
const MWWorld::Class &actorCls = actor.getClass();
|
||||
const ESM::Weapon *weapon = NULL;
|
||||
MWMechanics::WeaponType weaptype;
|
||||
float weapRange, weapSpeed = 1.0f;
|
||||
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
actorCls.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
|
||||
if (actor.getClass().hasInventoryStore(actor))
|
||||
// Get weapon characteristics
|
||||
if (actorCls.hasInventoryStore(actor))
|
||||
{
|
||||
MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState();
|
||||
MWMechanics::DrawState_ state = actorCls.getCreatureStats(actor).getDrawState();
|
||||
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
|
||||
actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
|
||||
actorCls.getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
|
||||
|
||||
//Get weapon speed and range
|
||||
MWWorld::ContainerStoreIterator weaponSlot =
|
||||
MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype);
|
||||
MWMechanics::getActiveWeapon(actorCls.getCreatureStats(actor), actorCls.getInventoryStore(actor), &weaptype);
|
||||
|
||||
if (weaptype == WeapType_HandToHand)
|
||||
{
|
||||
|
@ -225,36 +274,27 @@ namespace MWMechanics
|
|||
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
|
||||
}
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
|
||||
/*
|
||||
* Some notes on meanings of variables:
|
||||
*
|
||||
* rangeMelee:
|
||||
* rangeAttack:
|
||||
*
|
||||
* - Distance where attack using the actor's weapon is possible
|
||||
* - longer for ranged weapons (obviously?) vs. melee weapons
|
||||
* - Distance where attack using the actor's weapon is possible:
|
||||
* longer for ranged weapons (obviously?) vs. melee weapons
|
||||
* - Determined by weapon's reach parameter; hardcoded value
|
||||
* for ranged weapon and for creatures
|
||||
* - Once within this distance mFollowTarget is triggered
|
||||
* (TODO: check whether the follow logic still works for ranged
|
||||
* weapons, since rangeCloseup is set to zero)
|
||||
* - TODO: The variable name is confusing. It was ok when AiCombat only
|
||||
* had melee weapons but now that ranged weapons are supported that is
|
||||
* no longer the case. It should really be renamed to something
|
||||
* like rangeStrike - alternatively, keep this name for melee
|
||||
* weapons and use a different variable for tracking ranged weapon
|
||||
* distance (rangeRanged maybe?)
|
||||
*
|
||||
* rangeCloseup:
|
||||
* rangeFollow:
|
||||
*
|
||||
* - Applies to melee weapons or hand to hand only (or creatures without
|
||||
* weapons)
|
||||
* - Distance a little further away from the actor's weapon strike
|
||||
* i.e. rangeCloseup > rangeMelee for melee weapons
|
||||
* (the variable names make this simple concept counter-intuitive,
|
||||
* something like rangeMelee > rangeStrike may be better)
|
||||
* - Distance a little further away than the actor's weapon reach
|
||||
* i.e. rangeFollow > rangeAttack for melee weapons
|
||||
* - Hardcoded value (0 for ranged weapons)
|
||||
* - Once the target gets beyond this distance mFollowTarget is cleared
|
||||
* and a path to the target needs to be found
|
||||
* - TODO: Possibly rename this variable to rangeMelee or even rangeFollow
|
||||
*
|
||||
* mFollowTarget:
|
||||
*
|
||||
|
@ -263,58 +303,72 @@ namespace MWMechanics
|
|||
* available, since the default path without pathgrids is direct to
|
||||
* target even if LOS is not achieved)
|
||||
*/
|
||||
float rangeMelee;
|
||||
float rangeCloseUp;
|
||||
|
||||
float rangeAttack;
|
||||
float rangeFollow;
|
||||
bool distantCombat = false;
|
||||
if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown)
|
||||
if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown)
|
||||
{
|
||||
rangeMelee = 1000; // TODO: should depend on archer skill
|
||||
rangeCloseUp = 0; //doesn't needed when attacking from distance
|
||||
rangeAttack = 1000; // TODO: should depend on archer skill
|
||||
rangeFollow = 0; // not needed in ranged combat
|
||||
distantCombat = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeMelee = weapRange;
|
||||
rangeCloseUp = 300;
|
||||
rangeAttack = weapRange;
|
||||
rangeFollow = 300;
|
||||
}
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
Ogre::Vector3 vActorPos(pos.pos);
|
||||
Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
|
||||
|
||||
Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]);
|
||||
ESM::Position targetPos = mTarget.getRefData().getPosition();
|
||||
Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]);
|
||||
Ogre::Vector3 vDir = vDest - vStart;
|
||||
float distBetween = vDir.length();
|
||||
bool isStuck = false;
|
||||
float speed = 0.0f;
|
||||
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = actorCls.getSpeed(actor)) / 10.0f)
|
||||
isStuck = true;
|
||||
|
||||
mLastPos = pos;
|
||||
|
||||
// check if actor can move along z-axis
|
||||
bool canMoveByZ = (actorCls.canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||
|| MWBase::Environment::get().getWorld()->isFlying(actor);
|
||||
|
||||
// determine vertical angle to target
|
||||
// if actor can move along z-axis it will control movement dir
|
||||
// if can't - it will control correct aiming
|
||||
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget);
|
||||
|
||||
vDirToTarget.z = 0;
|
||||
float distToTarget = vDirToTarget.length();
|
||||
|
||||
// (within strike dist) || (not quite strike dist while following)
|
||||
if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) )
|
||||
if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) )
|
||||
{
|
||||
//Melee and Close-up combat
|
||||
vDir.z = 0;
|
||||
float dirLen = vDir.length();
|
||||
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
|
||||
mRotate = true;
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
|
||||
//bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
|
||||
// (not quite strike dist while following)
|
||||
if (mFollowTarget && distBetween > rangeMelee)
|
||||
if (mFollowTarget && distToTarget > rangeAttack)
|
||||
{
|
||||
//Close-up combat: just run up on target
|
||||
mMovement.mPosition[1] = 1;
|
||||
}
|
||||
else // (within strike dist)
|
||||
{
|
||||
//Melee: stop running and attack
|
||||
mMovement.mPosition[1] = 0;
|
||||
|
||||
// When attacking with a weapon, choose between slash, thrust or chop
|
||||
if (actor.getClass().hasInventoryStore(actor))
|
||||
chooseBestAttack(weapon, mMovement);
|
||||
// set slash/thrust/chop attack
|
||||
if (mAttack && !distantCombat) chooseBestAttack(weapon, mMovement);
|
||||
|
||||
if(mMovement.mPosition[0] || mMovement.mPosition[1])
|
||||
{
|
||||
mTimerCombatMove = 0.1f + 0.1f * static_cast<float>(rand())/RAND_MAX;
|
||||
mCombatMove = true;
|
||||
}
|
||||
else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5)))
|
||||
// only NPCs are smart enough to use dodge movements
|
||||
else if(actorCls.isNpc() && (!distantCombat || (distantCombat && distToTarget < rangeAttack/2)))
|
||||
{
|
||||
//apply sideway movement (kind of dodging) with some probability
|
||||
if(static_cast<float>(rand())/RAND_MAX < 0.25)
|
||||
|
@ -325,7 +379,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
if(distantCombat && distBetween < rangeMelee/4)
|
||||
if(distantCombat && distToTarget < rangeAttack/4)
|
||||
{
|
||||
mMovement.mPosition[1] = -1;
|
||||
}
|
||||
|
@ -335,56 +389,105 @@ namespace MWMechanics
|
|||
mFollowTarget = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
else // remote pathfinding
|
||||
{
|
||||
//target is at far distance: build path to target
|
||||
mFollowTarget = false;
|
||||
bool preferShortcut = false;
|
||||
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
|
||||
|
||||
buildNewPath(actor); //may fail to build a path, check before use
|
||||
|
||||
//delete visited path node
|
||||
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
|
||||
//if no new path leave mTargetAngle unchanged
|
||||
if(!mPathFinder.getPath().empty())
|
||||
if(mReadyToAttack) isStuck = false;
|
||||
|
||||
// check if shortcut is available
|
||||
if(!isStuck
|
||||
&& (!mForceNoShortcut
|
||||
|| (Ogre::Vector3(mShortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST)
|
||||
&& inLOS)
|
||||
{
|
||||
//try shortcut
|
||||
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
|
||||
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
|
||||
else
|
||||
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||
mRotate = true;
|
||||
if(speed == 0.0f) speed = actorCls.getSpeed(actor);
|
||||
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
||||
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
|
||||
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
|
||||
}
|
||||
|
||||
// don't use pathgrid when actor can move in 3 dimensions
|
||||
if(canMoveByZ) preferShortcut = true;
|
||||
|
||||
if(preferShortcut)
|
||||
{
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mForceNoShortcut = false;
|
||||
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
||||
mPathFinder.clearPath();
|
||||
}
|
||||
else // if shortcut failed stick to path grid
|
||||
{
|
||||
if(!isStuck && mShortcutFailPos.pos[0] == 0.0f && mShortcutFailPos.pos[1] == 0.0f && mShortcutFailPos.pos[2] == 0.0f)
|
||||
{
|
||||
mForceNoShortcut = true;
|
||||
mShortcutFailPos = pos;
|
||||
}
|
||||
|
||||
mFollowTarget = false;
|
||||
|
||||
buildNewPath(actor); //may fail to build a path, check before use
|
||||
|
||||
//delete visited path node
|
||||
mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
|
||||
// This works on the borders between the path grid and areas with no waypoints.
|
||||
if(inLOS && mPathFinder.getPath().size() > 1)
|
||||
{
|
||||
// get point just before target
|
||||
std::list<ESM::Pathgrid::Point>::const_iterator pntIter = --mPathFinder.getPath().end();
|
||||
--pntIter;
|
||||
Ogre::Vector3 vBeforeTarget = Ogre::Vector3(pntIter->mX, pntIter->mY, pntIter->mZ);
|
||||
|
||||
// if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
|
||||
if(distToTarget <= (vTargetPos - vBeforeTarget).length())
|
||||
{
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
preferShortcut = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no new path, then go straight on target
|
||||
if(!preferShortcut)
|
||||
{
|
||||
if(!mPathFinder.getPath().empty())
|
||||
mMovement.mRotation[2] = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||
else
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
}
|
||||
}
|
||||
|
||||
mMovement.mPosition[1] = 1;
|
||||
mReadyToAttack = false;
|
||||
}
|
||||
|
||||
if(distBetween > rangeMelee)
|
||||
if(distToTarget > rangeAttack && !distantCombat)
|
||||
{
|
||||
//special run attack; it shouldn't affect melee combat tactics
|
||||
if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1)
|
||||
if(actorCls.getMovementSettings(actor).mPosition[1] == 1)
|
||||
{
|
||||
//check if actor can overcome the distance = distToTarget - attackerWeapRange
|
||||
//less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing)
|
||||
//then start attacking
|
||||
float speed1 = cls.getSpeed(actor);
|
||||
float speed1 = actorCls.getSpeed(actor);
|
||||
float speed2 = mTarget.getClass().getSpeed(mTarget);
|
||||
if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0
|
||||
&& mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
|
||||
speed2 = 0;
|
||||
|
||||
float s1 = distBetween - weapRange;
|
||||
float s1 = distToTarget - weapRange;
|
||||
float t = s1/speed1;
|
||||
float s2 = speed2 * t;
|
||||
float t_swing = 0.17f/weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
|
||||
float t_swing = (MAX_ATTACK_DURATION/2) / weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
|
||||
if (t + s2/speed1 <= t_swing)
|
||||
{
|
||||
mReadyToAttack = true;
|
||||
if(mTimerAttack <= -attackPeriod)
|
||||
if(mTimerAttack <= -attacksPeriod)
|
||||
{
|
||||
mTimerAttack = 0.3f*static_cast<float>(rand())/RAND_MAX;
|
||||
mStrike = true;
|
||||
mTimerAttack = MAX_ATTACK_DURATION * static_cast<float>(rand())/RAND_MAX;
|
||||
mAttack = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +497,7 @@ namespace MWMechanics
|
|||
// coded at 250ms or 1/4 second
|
||||
//
|
||||
// TODO: Add a parameter to vary DURATION_SAME_SPOT?
|
||||
if((distBetween > rangeMelee || mFollowTarget) &&
|
||||
if((distToTarget > rangeAttack || mFollowTarget) &&
|
||||
mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
|
||||
{
|
||||
// first check if we're walking into a door
|
||||
|
@ -406,12 +509,11 @@ namespace MWMechanics
|
|||
// Check all the doors in this cell
|
||||
mDoors = cell->get<ESM::Door>(); // update
|
||||
mDoorIter = mDoors.mList.begin();
|
||||
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
|
||||
for (; mDoorIter != mDoors.mList.end(); ++mDoorIter)
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
|
||||
float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility
|
||||
if(actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr &&
|
||||
if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr &&
|
||||
ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening
|
||||
{
|
||||
//std::cout<<"closed door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
|
||||
|
@ -441,11 +543,10 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
|
||||
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
|
||||
float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility
|
||||
// TODO: add reaction to checking open doors
|
||||
if(mBackOffDoor &&
|
||||
actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr)
|
||||
vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr)
|
||||
{
|
||||
mMovement.mPosition[1] = -0.2; // back off, but slowly
|
||||
}
|
||||
|
@ -454,47 +555,38 @@ namespace MWMechanics
|
|||
ref.mData.getLocalRotation().rot[2] >= 1)
|
||||
{
|
||||
mDoorIter = mDoors.mList.end();
|
||||
mBackOffDoor = false;
|
||||
mBackOffDoor = false;
|
||||
//std::cout<<"open door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
|
||||
mMovement.mPosition[1] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMovement.mPosition[1] = 1; // FIXME: oscillation?
|
||||
}
|
||||
|
||||
actor.getClass().getMovementSettings(actor) = mMovement;
|
||||
// these lines break ranged combat distance keeping
|
||||
//else
|
||||
//{
|
||||
// mMovement.mPosition[1] = 1; // FIXME: oscillation?
|
||||
//}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AiCombat::buildNewPath(const MWWorld::Ptr& actor)
|
||||
{
|
||||
//Construct path to target
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = mTarget.getRefData().getPosition().pos[0];
|
||||
dest.mY = mTarget.getRefData().getPosition().pos[1];
|
||||
dest.mZ = mTarget.getRefData().getPosition().pos[2];
|
||||
Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ);
|
||||
Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos);
|
||||
|
||||
float dist = -1; //hack to indicate first time, to construct a new path
|
||||
float dist;
|
||||
|
||||
if(!mPathFinder.getPath().empty())
|
||||
{
|
||||
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
|
||||
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
|
||||
dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length());
|
||||
dist = (newPathTarget - currPathTarget).length();
|
||||
}
|
||||
else dist = 1e+38F; // necessarily construct a new path
|
||||
|
||||
float targetPosThreshold;
|
||||
bool isOutside = actor.getCell()->getCell()->isExterior();
|
||||
if (isOutside)
|
||||
targetPosThreshold = 300;
|
||||
else
|
||||
targetPosThreshold = 100;
|
||||
float targetPosThreshold = (actor.getCell()->getCell()->isExterior())? 300 : 100;
|
||||
|
||||
if((dist < 0) || (dist > targetPosThreshold))
|
||||
//construct new path only if target has moved away more than on [targetPosThreshold]
|
||||
if(dist > targetPosThreshold)
|
||||
{
|
||||
//construct new path only if target has moved away more than on <targetPosThreshold>
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
|
||||
ESM::Pathgrid::Point start;
|
||||
|
@ -502,17 +594,18 @@ namespace MWMechanics
|
|||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = newPathTarget.x;
|
||||
dest.mY = newPathTarget.y;
|
||||
dest.mZ = newPathTarget.z;
|
||||
|
||||
if(!mPathFinder.isPathConstructed())
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
else
|
||||
{
|
||||
PathFinder newPathFinder;
|
||||
newPathFinder.buildPath(start, dest, actor.getCell(), isOutside);
|
||||
newPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
|
||||
//TO EXPLORE:
|
||||
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
|
||||
//not the actual path length. Here we should know if the new path is actually more effective.
|
||||
//if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
|
||||
if(!mPathFinder.getPath().empty())
|
||||
{
|
||||
newPathFinder.syncStart(mPathFinder.getPath());
|
||||
|
|
|
@ -39,17 +39,16 @@ namespace MWMechanics
|
|||
// when mCombatMove is true
|
||||
float mTimerCombatMove;
|
||||
|
||||
// the z rotation angle (degrees) we want to reach
|
||||
// used every frame when mRotate is true
|
||||
float mTargetAngle;
|
||||
|
||||
// AiCombat states
|
||||
bool mReadyToAttack, mStrike;
|
||||
bool mReadyToAttack, mAttack;
|
||||
bool mFollowTarget;
|
||||
bool mCombatMove;
|
||||
bool mBackOffDoor;
|
||||
bool mRotate;
|
||||
|
||||
bool mForceNoShortcut;
|
||||
ESM::Position mShortcutFailPos;
|
||||
|
||||
ESM::Position mLastPos;
|
||||
MWMechanics::Movement mMovement;
|
||||
MWWorld::Ptr mTarget;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace MWMechanics
|
|||
TypeIdFollow = 3,
|
||||
TypeIdActivate = 4,
|
||||
TypeIdCombat = 5,
|
||||
TypeIdPersue = 6
|
||||
TypeIdPursue = 6
|
||||
};
|
||||
|
||||
virtual ~AiPackage();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "aipersue.hpp"
|
||||
#include "aipursue.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -11,15 +11,15 @@
|
|||
#include "movement.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
||||
MWMechanics::AiPersue::AiPersue(const std::string &objectId)
|
||||
MWMechanics::AiPursue::AiPursue(const std::string &objectId)
|
||||
: mObjectId(objectId)
|
||||
{
|
||||
}
|
||||
MWMechanics::AiPersue *MWMechanics::AiPersue::clone() const
|
||||
MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
|
||||
{
|
||||
return new AiPersue(*this);
|
||||
return new AiPursue(*this);
|
||||
}
|
||||
bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
|
@ -52,11 +52,13 @@ bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor, float duration)
|
|||
}
|
||||
}
|
||||
|
||||
// Big TODO: Sync this with current AiFollow. Move common code to a shared base class or helpers (applies to all AI packages, way too much duplicated code)
|
||||
|
||||
MWWorld::Ptr target = world->getPtr(mObjectId,false);
|
||||
ESM::Position targetPos = target.getRefData().getPosition();
|
||||
|
||||
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
|
||||
if(!mPathFinder.isPathConstructed() || cellChange)
|
||||
if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
{
|
||||
mCellX = cell->mData.mX;
|
||||
mCellY = cell->mData.mY;
|
||||
|
@ -76,15 +78,7 @@ bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor, float duration)
|
|||
|
||||
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
|
||||
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
|
||||
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200)
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
MWWorld::Ptr target = world->getPtr(mObjectId,false);
|
||||
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 100*100)
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
MWWorld::Ptr target = world->getPtr(mObjectId,false);
|
||||
|
@ -100,7 +94,7 @@ bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor, float duration)
|
|||
return false;
|
||||
}
|
||||
|
||||
int MWMechanics::AiPersue::getTypeId() const
|
||||
int MWMechanics::AiPursue::getTypeId() const
|
||||
{
|
||||
return TypeIdPersue;
|
||||
return TypeIdPursue;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef GAME_MWMECHANICS_AIPERSUE_H
|
||||
#define GAME_MWMECHANICS_AIPERSUE_H
|
||||
#ifndef GAME_MWMECHANICS_AIPURSUE_H
|
||||
#define GAME_MWMECHANICS_AIPURSUE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include <string>
|
||||
|
@ -9,11 +9,11 @@
|
|||
namespace MWMechanics
|
||||
{
|
||||
|
||||
class AiPersue : public AiPackage
|
||||
class AiPursue : public AiPackage
|
||||
{
|
||||
public:
|
||||
AiPersue(const std::string &objectId);
|
||||
virtual AiPersue *clone() const;
|
||||
AiPursue(const std::string &objectId);
|
||||
virtual AiPursue *clone() const;
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
virtual int getTypeId() const;
|
|
@ -38,7 +38,7 @@ MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& s
|
|||
copy (sequence);
|
||||
mDone = sequence.mDone;
|
||||
}
|
||||
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ int MWMechanics::AiSequence::getTypeId() const
|
|||
{
|
||||
if (mPackages.empty())
|
||||
return -1;
|
||||
|
||||
|
||||
return mPackages.front()->getTypeId();
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,9 @@ void MWMechanics::AiSequence::stopCombat()
|
|||
}
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stopPersue()
|
||||
void MWMechanics::AiSequence::stopPursuit()
|
||||
{
|
||||
while (getTypeId() == AiPackage::TypeIdPersue)
|
||||
while (getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
delete *mPackages.begin();
|
||||
mPackages.erase (mPackages.begin());
|
||||
|
@ -93,16 +93,21 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
|||
{
|
||||
if (!mPackages.empty())
|
||||
{
|
||||
mLastAiPackage = mPackages.front()->getTypeId();
|
||||
if (mPackages.front()->execute (actor,duration))
|
||||
MWMechanics::AiPackage* package = mPackages.front();
|
||||
mLastAiPackage = package->getTypeId();
|
||||
if (package->execute (actor,duration))
|
||||
{
|
||||
delete *mPackages.begin();
|
||||
mPackages.erase (mPackages.begin());
|
||||
// To account for the rare case where AiPackage::execute() queued another AI package
|
||||
// (e.g. AiPursue executing a dialogue script that uses startCombat)
|
||||
std::list<MWMechanics::AiPackage*>::iterator toRemove =
|
||||
std::find(mPackages.begin(), mPackages.end(), package);
|
||||
mPackages.erase(toRemove);
|
||||
delete package;
|
||||
mDone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mDone = false;
|
||||
mDone = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +121,19 @@ void MWMechanics::AiSequence::clear()
|
|||
mPackages.clear();
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stack (const AiPackage& package)
|
||||
void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
{
|
||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); it++)
|
||||
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
// Notify AiWander of our current position so we can return to it after combat finished
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
||||
}
|
||||
}
|
||||
|
||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if(mPackages.front()->getPriority() <= package.getPriority())
|
||||
{
|
||||
|
|
|
@ -50,8 +50,8 @@ namespace MWMechanics
|
|||
void stopCombat();
|
||||
///< Removes all combat packages until first non-combat or stack empty.
|
||||
|
||||
void stopPersue();
|
||||
///< Removes all persue packages until first non-persue or stack empty.
|
||||
void stopPursuit();
|
||||
///< Removes all pursue packages until first non-pursue or stack empty.
|
||||
|
||||
bool isPackageDone() const;
|
||||
///< Has a package been completed during the last update?
|
||||
|
@ -62,7 +62,7 @@ namespace MWMechanics
|
|||
void clear();
|
||||
///< Remove all packages.
|
||||
|
||||
void stack (const AiPackage& package);
|
||||
void stack (const AiPackage& package, const MWWorld::Ptr& actor);
|
||||
///< Add \a package to the front of the sequence (suspends current package)
|
||||
|
||||
void queue (const AiPackage& package);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "aiwander.hpp"
|
||||
|
||||
#include <OgreVector3.h>
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -37,6 +38,8 @@ namespace MWMechanics
|
|||
, mRotate(false)
|
||||
, mTargetAngle(0)
|
||||
, mSaidGreeting(false)
|
||||
, mHasReturnPosition(false)
|
||||
, mReturnPosition(0,0,0)
|
||||
{
|
||||
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
||||
{
|
||||
|
@ -211,7 +214,7 @@ namespace MWMechanics
|
|||
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||
// epsilon... TODO: a proper fix might be in either the physics or the
|
||||
// animation subsystem
|
||||
if (zTurn(actor, Ogre::Degree(mTargetAngle), Ogre::Degree(12)))
|
||||
if (zTurn(actor, Ogre::Degree(mTargetAngle), Ogre::Degree(5)))
|
||||
mRotate = false;
|
||||
}
|
||||
|
||||
|
@ -330,21 +333,44 @@ namespace MWMechanics
|
|||
if(mDistance && cellChange)
|
||||
mDistance = 0;
|
||||
|
||||
// For stationary NPCs, move back to the starting location if another AiPackage moved us elsewhere
|
||||
if (cellChange)
|
||||
mHasReturnPosition = false;
|
||||
if (mDistance == 0 && mHasReturnPosition && Ogre::Vector3(pos.pos).squaredDistance(mReturnPosition) > 20*20)
|
||||
{
|
||||
mChooseAction = false;
|
||||
mIdleNow = false;
|
||||
|
||||
if (!mPathFinder.isPathConstructed())
|
||||
{
|
||||
Ogre::Vector3 destNodePos = mReturnPosition;
|
||||
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = destNodePos[0];
|
||||
dest.mY = destNodePos[1];
|
||||
dest.mZ = destNodePos[2];
|
||||
|
||||
// actor position is already in world co-ordinates
|
||||
ESM::Pathgrid::Point start;
|
||||
start.mX = pos.pos[0];
|
||||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
// don't take shortcuts for wandering
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||
|
||||
if(mPathFinder.isPathConstructed())
|
||||
{
|
||||
mMoveNow = false;
|
||||
mWalking = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mChooseAction)
|
||||
{
|
||||
mPlayedIdle = 0;
|
||||
unsigned short idleRoll = 0;
|
||||
|
||||
for(unsigned int counter = 0; counter < mIdle.size(); counter++)
|
||||
{
|
||||
unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter];
|
||||
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
|
||||
if(randSelect < idleChance && randSelect > idleRoll)
|
||||
{
|
||||
mPlayedIdle = counter+2;
|
||||
idleRoll = randSelect;
|
||||
}
|
||||
}
|
||||
getRandomIdle(); // NOTE: sets mPlayedIdle with a random selection
|
||||
|
||||
if(!mPlayedIdle && mDistance)
|
||||
{
|
||||
|
@ -375,7 +401,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState()))
|
||||
if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState() && mDistance))
|
||||
{
|
||||
// Play a random voice greeting if the player gets too close
|
||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||
|
@ -395,6 +421,8 @@ namespace MWMechanics
|
|||
mMoveNow = false;
|
||||
mWalking = false;
|
||||
mObstacleCheck.clear();
|
||||
mIdleNow = true;
|
||||
getRandomIdle();
|
||||
}
|
||||
|
||||
if(!mRotate)
|
||||
|
@ -402,11 +430,11 @@ namespace MWMechanics
|
|||
Ogre::Vector3 dir = playerPos - actorPos;
|
||||
float length = dir.length();
|
||||
|
||||
// FIXME: horrible hack
|
||||
float faceAngle = Ogre::Radian(Ogre::Math::ACos(dir.y / length) *
|
||||
((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees();
|
||||
float actorAngle = actor.getRefData().getBaseNode()->getOrientation().getRoll().valueDegrees();
|
||||
// an attempt at reducing the turning animation glitch
|
||||
if(abs(faceAngle) > 10)
|
||||
if(abs(abs(faceAngle) - abs(actorAngle)) >= 5) // TODO: is there a better way?
|
||||
{
|
||||
mTargetAngle = faceAngle;
|
||||
mRotate = true;
|
||||
|
@ -432,7 +460,8 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
if(!checkIdle(actor, mPlayedIdle))
|
||||
// FIXME: don't stay forever
|
||||
if(!checkIdle(actor, mPlayedIdle) && playerDistSqr > helloDistance*helloDistance)
|
||||
{
|
||||
mPlayedIdle = 0;
|
||||
mIdleNow = false;
|
||||
|
@ -503,6 +532,7 @@ namespace MWMechanics
|
|||
mMoveNow = false;
|
||||
mWalking = false;
|
||||
mChooseAction = true;
|
||||
mHasReturnPosition = false;
|
||||
}
|
||||
|
||||
return false; // AiWander package not yet completed
|
||||
|
@ -586,5 +616,30 @@ namespace MWMechanics
|
|||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void AiWander::setReturnPosition(const Ogre::Vector3& position)
|
||||
{
|
||||
if (!mHasReturnPosition)
|
||||
{
|
||||
mHasReturnPosition = true;
|
||||
mReturnPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
void AiWander::getRandomIdle()
|
||||
{
|
||||
unsigned short idleRoll = 0;
|
||||
|
||||
for(unsigned int counter = 0; counter < mIdle.size(); counter++)
|
||||
{
|
||||
unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter];
|
||||
unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
|
||||
if(randSelect < idleChance && randSelect > idleRoll)
|
||||
{
|
||||
mPlayedIdle = counter+2;
|
||||
idleRoll = randSelect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
#define GAME_MWMECHANICS_AIWANDER_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include "pathfinding.hpp"
|
||||
#include "obstacle.hpp"
|
||||
|
||||
|
@ -22,10 +25,15 @@ namespace MWMechanics
|
|||
virtual int getTypeId() const;
|
||||
///< 0: Wander
|
||||
|
||||
void setReturnPosition (const Ogre::Vector3& position);
|
||||
///< Set the position to return to for a stationary (non-wandering) actor, in case
|
||||
/// another AI package moved the actor elsewhere
|
||||
|
||||
private:
|
||||
void stopWalking(const MWWorld::Ptr& actor);
|
||||
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||
void getRandomIdle();
|
||||
|
||||
int mDistance; // how far the actor can wander from the spawn point
|
||||
int mDuration;
|
||||
|
@ -38,6 +46,10 @@ namespace MWMechanics
|
|||
float mGreetDistanceReset;
|
||||
float mChance;
|
||||
|
||||
bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position,
|
||||
// if we had the actor in the AiWander constructor...
|
||||
Ogre::Vector3 mReturnPosition;
|
||||
|
||||
// Cached current cell location
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
|
|
|
@ -489,12 +489,13 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
mIdleState = CharState_Idle;
|
||||
}
|
||||
|
||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
|
||||
if(mDeathState != CharState_None)
|
||||
{
|
||||
playRandomDeath(1.0f);
|
||||
}
|
||||
else
|
||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController()
|
||||
|
@ -1010,6 +1011,7 @@ void CharacterController::update(float duration)
|
|||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
vec = Ogre::Vector3(0.0f);
|
||||
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
|
||||
|
||||
mMovementSpeed = cls.getSpeed(mPtr);
|
||||
|
||||
vec.x *= mMovementSpeed;
|
||||
|
@ -1175,7 +1177,7 @@ void CharacterController::update(float duration)
|
|||
}
|
||||
else
|
||||
{
|
||||
if(!(vec.z > 0.0f))
|
||||
if(!(vec.z > 0.0f))
|
||||
mJumpState = JumpState_None;
|
||||
vec.z = 0.0f;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace MWMechanics
|
|||
mAttacked (false), mHostile (false),
|
||||
mAttackingOrSpell(false),
|
||||
mIsWerewolf(false),
|
||||
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false),
|
||||
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
|
||||
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
|
@ -387,6 +387,8 @@ namespace MWMechanics
|
|||
void CreatureStats::setKnockedDown(bool value)
|
||||
{
|
||||
mKnockdown = value;
|
||||
if(!value) //Resets the "OverOneFrame" flag
|
||||
setKnockedDownOverOneFrame(false);
|
||||
}
|
||||
|
||||
bool CreatureStats::getKnockedDown() const
|
||||
|
@ -394,6 +396,23 @@ namespace MWMechanics
|
|||
return mKnockdown;
|
||||
}
|
||||
|
||||
void CreatureStats::setKnockedDownOneFrame(bool value)
|
||||
{
|
||||
mKnockdownOneFrame = value;
|
||||
}
|
||||
|
||||
bool CreatureStats::getKnockedDownOneFrame() const
|
||||
{
|
||||
return mKnockdownOneFrame;
|
||||
}
|
||||
|
||||
void CreatureStats::setKnockedDownOverOneFrame(bool value) {
|
||||
mKnockdownOverOneFrame = value;
|
||||
}
|
||||
bool CreatureStats::getKnockedDownOverOneFrame() const {
|
||||
return mKnockdownOverOneFrame;
|
||||
}
|
||||
|
||||
void CreatureStats::setHitRecovery(bool value)
|
||||
{
|
||||
mHitRecovery = value;
|
||||
|
@ -479,7 +498,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Relates to NPC gold reset delay
|
||||
void CreatureStats::setTradeTime(MWWorld::TimeStamp tradeTime)
|
||||
void CreatureStats::setTradeTime(MWWorld::TimeStamp tradeTime)
|
||||
{
|
||||
mTradeTime = tradeTime;
|
||||
}
|
||||
|
@ -489,11 +508,11 @@ namespace MWMechanics
|
|||
return mTradeTime;
|
||||
}
|
||||
|
||||
void CreatureStats::setGoldPool(int pool)
|
||||
void CreatureStats::setGoldPool(int pool)
|
||||
{
|
||||
mGoldPool = pool;
|
||||
}
|
||||
int CreatureStats::getGoldPool() const
|
||||
int CreatureStats::getGoldPool() const
|
||||
{
|
||||
return mGoldPool;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace MWMechanics
|
|||
bool mHostile;
|
||||
bool mAttackingOrSpell;
|
||||
bool mKnockdown;
|
||||
bool mKnockdownOneFrame;
|
||||
bool mKnockdownOverOneFrame;
|
||||
bool mHitRecovery;
|
||||
bool mBlock;
|
||||
unsigned int mMovementFlags;
|
||||
|
@ -188,7 +190,14 @@ namespace MWMechanics
|
|||
float getEvasion() const;
|
||||
|
||||
void setKnockedDown(bool value);
|
||||
///Returns true for the entire duration of the actor being knocked down
|
||||
bool getKnockedDown() const;
|
||||
void setKnockedDownOneFrame(bool value);
|
||||
///Returns true only for the first frame of the actor being knocked out; used for "onKnockedOut" command
|
||||
bool getKnockedDownOneFrame() const;
|
||||
void setKnockedDownOverOneFrame(bool value);
|
||||
///Returns true for all but the first frame of being knocked out; used to know to not reset mKnockedDownOneFrame
|
||||
bool getKnockedDownOverOneFrame() const;
|
||||
void setHitRecovery(bool value);
|
||||
bool getHitRecovery() const;
|
||||
void setBlock(bool value);
|
||||
|
|
|
@ -298,13 +298,15 @@ namespace MWMechanics
|
|||
|
||||
if(stats.getTimeToStartDrowning() != mWatchedStats.getTimeToStartDrowning())
|
||||
{
|
||||
const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fHoldBreathTime")->getFloat();
|
||||
mWatchedStats.setTimeToStartDrowning(stats.getTimeToStartDrowning());
|
||||
if(stats.getTimeToStartDrowning() >= 20.0f)
|
||||
if(stats.getTimeToStartDrowning() >= fHoldBreathTime)
|
||||
winMgr->setDrowningBarVisibility(false);
|
||||
else
|
||||
{
|
||||
winMgr->setDrowningBarVisibility(true);
|
||||
winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning());
|
||||
winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,6 +340,8 @@ namespace MWMechanics
|
|||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||
if (enchantItem != inv.end())
|
||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||
else if (winMgr->getSelectedSpell() == "")
|
||||
winMgr->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
if (mUpdatePlayer)
|
||||
|
@ -856,7 +860,8 @@ namespace MWMechanics
|
|||
// Find an actor who witnessed the crime
|
||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if (*it == ptr) continue; // not the player
|
||||
if ( *it == ptr
|
||||
|| !it->getClass().isNpc()) continue; // not the player and is an NPC
|
||||
|
||||
// Was the crime seen?
|
||||
if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) ||
|
||||
|
@ -872,7 +877,8 @@ namespace MWMechanics
|
|||
// Tell everyone, including yourself
|
||||
for (std::vector<MWWorld::Ptr>::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1)
|
||||
{
|
||||
if (*it1 == ptr) continue; // not the player
|
||||
if ( *it == ptr
|
||||
|| !it->getClass().isNpc()) continue; // not the player and is an NPC
|
||||
|
||||
// TODO: Add more messages
|
||||
if (type == OT_Theft)
|
||||
|
|
|
@ -325,22 +325,22 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// used by AiCombat, see header for the rationale
|
||||
void PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
|
||||
bool PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
|
||||
{
|
||||
if (mPath.size() < 2)
|
||||
return; //nothing to pop
|
||||
return false; //nothing to pop
|
||||
|
||||
std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
|
||||
std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();
|
||||
|
||||
if( (*iter).mX == oldStart->mX
|
||||
&& (*iter).mY == oldStart->mY
|
||||
&& (*iter).mZ == oldStart->mZ
|
||||
&& (*iter).mAutogenerated == oldStart->mAutogenerated
|
||||
&& (*iter).mConnectionNum == oldStart->mConnectionNum )
|
||||
&& (*iter).mZ == oldStart->mZ)
|
||||
{
|
||||
mPath.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,16 +57,20 @@ namespace MWMechanics
|
|||
return mPath.size();
|
||||
}
|
||||
|
||||
std::list<ESM::Pathgrid::Point> getPath() const
|
||||
const std::list<ESM::Pathgrid::Point>& getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
// When first point of newly created path is the nearest to actor point,
|
||||
// then a situation can occure when this point is undesirable
|
||||
// (if the 2nd point of new path == the 1st point of old path)
|
||||
// This functions deletes that point.
|
||||
void syncStart(const std::list<ESM::Pathgrid::Point> &path);
|
||||
/** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
|
||||
@note
|
||||
If the first point is chosen as the nearest one
|
||||
the situation can occur when the 1st point of the new path is undesirable
|
||||
(i.e. the 2nd point of new path == the 1st point of old path).
|
||||
@param path - old path
|
||||
@return true if such point was found and deleted
|
||||
*/
|
||||
bool syncStart(const std::list<ESM::Pathgrid::Point> &path);
|
||||
|
||||
void addPointToPath(ESM::Pathgrid::Point &point)
|
||||
{
|
||||
|
|
|
@ -296,7 +296,7 @@ namespace MWMechanics
|
|||
// add this edge to openset, lowest cost goes to the front
|
||||
// TODO: if this causes performance problems a hash table may help
|
||||
std::list<int>::iterator it = openset.begin();
|
||||
for(it = openset.begin(); it!= openset.end(); it++)
|
||||
for(it = openset.begin(); it!= openset.end(); ++it)
|
||||
{
|
||||
if(fScore[*it] > fScore[dest])
|
||||
break;
|
||||
|
|
|
@ -401,10 +401,10 @@ namespace MWMechanics
|
|||
if (!exploded)
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName);
|
||||
|
||||
if (reflectedEffects.mList.size())
|
||||
if (!reflectedEffects.mList.empty())
|
||||
inflict(caster, target, reflectedEffects, range, true);
|
||||
|
||||
if (appliedLastingEffects.size())
|
||||
if (!appliedLastingEffects.empty())
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, caster.getRefData().getHandle());
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace MWMechanics
|
|||
if (spell->mData.mType == ESM::Spell::ST_Disease)
|
||||
mSpells.erase(iter++);
|
||||
else
|
||||
iter++;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ namespace MWMechanics
|
|||
if (spell->mData.mType == ESM::Spell::ST_Blight)
|
||||
mSpells.erase(iter++);
|
||||
else
|
||||
iter++;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ namespace MWMechanics
|
|||
if (Misc::StringUtils::ciEqual(spell->mId, "corprus"))
|
||||
mSpells.erase(iter++);
|
||||
else
|
||||
iter++;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ namespace MWMechanics
|
|||
if (spell->mData.mType == ESM::Spell::ST_Curse)
|
||||
mSpells.erase(iter++);
|
||||
else
|
||||
iter++;
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
namespace MWMechanics
|
||||
{
|
||||
|
||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, Ogre::Degree epsilon)
|
||||
bool smoothTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, int axis, Ogre::Degree epsilon)
|
||||
{
|
||||
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]);
|
||||
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[axis]);
|
||||
Ogre::Radian diff (targetAngle - currentAngle);
|
||||
if (diff >= Ogre::Degree(180))
|
||||
{
|
||||
|
@ -30,13 +30,17 @@ bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, Ogre::Degree eps
|
|||
if (absDiff < epsilon)
|
||||
return true;
|
||||
|
||||
// Max. speed of 10 radian per sec
|
||||
Ogre::Radian limit = Ogre::Radian(10) * MWBase::Environment::get().getFrameDuration();
|
||||
Ogre::Radian limit = MAX_VEL_ANGULAR * MWBase::Environment::get().getFrameDuration();
|
||||
if (absDiff > limit)
|
||||
diff = Ogre::Math::Sign(diff) * limit;
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mRotation[2] = diff.valueRadians();
|
||||
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff.valueRadians();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, Ogre::Degree epsilon)
|
||||
{
|
||||
return smoothTurn(actor, targetAngle, 2, epsilon);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,17 @@ class Ptr;
|
|||
namespace MWMechanics
|
||||
{
|
||||
|
||||
// Max rotating speed, radian/sec
|
||||
const Ogre::Radian MAX_VEL_ANGULAR(10);
|
||||
|
||||
/// configure rotation settings for an actor to reach this target angle (eventually)
|
||||
/// @return have we reached the target angle?
|
||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle,
|
||||
Ogre::Degree epsilon = Ogre::Degree(0.5));
|
||||
|
||||
bool smoothTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, int axis,
|
||||
Ogre::Degree epsilon = Ogre::Degree(0.5));
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -411,7 +411,7 @@ Ogre::Node *Animation::getNode(const std::string &name)
|
|||
NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname)
|
||||
{
|
||||
NifOgre::TextKeyMap::const_iterator iter(keys.begin());
|
||||
for(;iter != keys.end();iter++)
|
||||
for(;iter != keys.end();++iter)
|
||||
{
|
||||
if(iter->second.compare(0, groupname.size(), groupname) == 0 &&
|
||||
iter->second.compare(groupname.size(), 2, ": ") == 0)
|
||||
|
@ -424,7 +424,7 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex
|
|||
bool Animation::hasAnimation(const std::string &anim)
|
||||
{
|
||||
AnimSourceList::const_iterator iter(mAnimSources.begin());
|
||||
for(;iter != mAnimSources.end();iter++)
|
||||
for(;iter != mAnimSources.end();++iter)
|
||||
{
|
||||
const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys;
|
||||
if(findGroupStart(keys, anim) != keys.end())
|
||||
|
@ -465,7 +465,7 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node
|
|||
stoptime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
keyiter++;
|
||||
++keyiter;
|
||||
}
|
||||
|
||||
if(stoptime > starttime)
|
||||
|
@ -585,13 +585,13 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s
|
|||
std::string starttag = groupname+": "+start;
|
||||
NifOgre::TextKeyMap::const_iterator startkey(groupstart);
|
||||
while(startkey != keys.end() && startkey->second != starttag)
|
||||
startkey++;
|
||||
++startkey;
|
||||
if(startkey == keys.end() && start == "loop start")
|
||||
{
|
||||
starttag = groupname+": start";
|
||||
startkey = groupstart;
|
||||
while(startkey != keys.end() && startkey->second != starttag)
|
||||
startkey++;
|
||||
++startkey;
|
||||
}
|
||||
if(startkey == keys.end())
|
||||
return false;
|
||||
|
@ -603,7 +603,7 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s
|
|||
// The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop".
|
||||
// Why, just why? :(
|
||||
&& (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag))
|
||||
stopkey++;
|
||||
++stopkey;
|
||||
if(stopkey == keys.end())
|
||||
return false;
|
||||
|
||||
|
@ -627,7 +627,7 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s
|
|||
state.mLoopStartTime = key->first;
|
||||
else if(key->second == loopstoptag)
|
||||
state.mLoopStopTime = key->first;
|
||||
key++;
|
||||
++key;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -776,7 +776,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
|||
|
||||
/* Look in reverse; last-inserted source has priority. */
|
||||
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
|
||||
for(;iter != mAnimSources.rend();iter++)
|
||||
for(;iter != mAnimSources.rend();++iter)
|
||||
{
|
||||
const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys;
|
||||
AnimState state;
|
||||
|
@ -795,7 +795,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
|||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, groupname, textkey);
|
||||
textkey++;
|
||||
++textkey;
|
||||
}
|
||||
|
||||
if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0)
|
||||
|
@ -810,7 +810,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
|||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, groupname, textkey);
|
||||
textkey++;
|
||||
++textkey;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -965,7 +965,7 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, stateiter->first, textkey);
|
||||
textkey++;
|
||||
++textkey;
|
||||
}
|
||||
|
||||
if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0)
|
||||
|
@ -979,7 +979,7 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, stateiter->first, textkey);
|
||||
textkey++;
|
||||
++textkey;
|
||||
}
|
||||
|
||||
if(state.mTime >= state.mLoopStopTime)
|
||||
|
|
|
@ -34,6 +34,10 @@ namespace MWRender
|
|||
|
||||
virtual void rebuild();
|
||||
|
||||
private:
|
||||
CharacterPreview(const CharacterPreview&);
|
||||
CharacterPreview& operator=(const CharacterPreview&);
|
||||
|
||||
protected:
|
||||
virtual bool renderHeadOnly() { return false; }
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid)
|
|||
uint32 startIndex = 0;
|
||||
for(ESM::Pathgrid::PointList::const_iterator it = pathgrid->mPoints.begin();
|
||||
it != pathgrid->mPoints.end();
|
||||
it++, startIndex += 6)
|
||||
++it, startIndex += 6)
|
||||
{
|
||||
Vector3 pointPos(it->mX, it->mY, it->mZ);
|
||||
|
||||
|
|
|
@ -240,25 +240,25 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::CellStore* cell)
|
|||
void Objects::enableLights()
|
||||
{
|
||||
PtrAnimationMap::const_iterator it = mObjects.begin();
|
||||
for(;it != mObjects.end();it++)
|
||||
for(;it != mObjects.end();++it)
|
||||
it->second->enableLights(true);
|
||||
}
|
||||
|
||||
void Objects::disableLights()
|
||||
{
|
||||
PtrAnimationMap::const_iterator it = mObjects.begin();
|
||||
for(;it != mObjects.end();it++)
|
||||
for(;it != mObjects.end();++it)
|
||||
it->second->enableLights(false);
|
||||
}
|
||||
|
||||
void Objects::update(float dt, Ogre::Camera* camera)
|
||||
{
|
||||
PtrAnimationMap::const_iterator it = mObjects.begin();
|
||||
for(;it != mObjects.end();it++)
|
||||
for(;it != mObjects.end();++it)
|
||||
it->second->runAnimation(dt);
|
||||
|
||||
it = mObjects.begin();
|
||||
for(;it != mObjects.end();it++)
|
||||
for(;it != mObjects.end();++it)
|
||||
it->second->preRender(camera);
|
||||
|
||||
}
|
||||
|
|
|
@ -563,7 +563,8 @@ void RenderingManager::configureAmbient(MWWorld::CellStore &mCell)
|
|||
Ogre::ColourValue colour;
|
||||
colour.setAsABGR (mCell.getCell()->mAmbi.mSunlight);
|
||||
mSun->setDiffuseColour (colour);
|
||||
mSun->setDirection(0,-1,0);
|
||||
mSun->setDirection(1,-1,-1);
|
||||
sunEnable(false);
|
||||
}
|
||||
}
|
||||
// Switch through lighting modes.
|
||||
|
|
|
@ -28,7 +28,9 @@ RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager)
|
|||
mRippleAreaLength(1000),
|
||||
mImpulseSize(20),
|
||||
mTexelOffset(0,0),
|
||||
mFirstUpdate(true)
|
||||
mFirstUpdate(true),
|
||||
mRectangle(NULL),
|
||||
mImpulse(NULL)
|
||||
{
|
||||
Ogre::AxisAlignedBox aabInf;
|
||||
aabInf.setInfinite();
|
||||
|
@ -105,6 +107,7 @@ RippleSimulation::RippleSimulation(Ogre::SceneManager* mainSceneManager)
|
|||
RippleSimulation::~RippleSimulation()
|
||||
{
|
||||
delete mRectangle;
|
||||
delete mImpulse;
|
||||
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
}
|
||||
|
|
|
@ -6,14 +6,9 @@
|
|||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreRenderWindow.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreTechnique.h>
|
||||
#include <OgreRectangle2D.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreStringConverter.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
|
@ -21,9 +16,6 @@
|
|||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwsound/sound_decoder.hpp"
|
||||
#include "../mwsound/sound.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <BaseTsd.h>
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiActivate activatePackage(objectID);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(activatePackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr);
|
||||
std::cout << "AiActivate" << std::endl;
|
||||
}
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiTravel travelPackage(x, y, z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(travelPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr);
|
||||
|
||||
std::cout << "AiTravel: " << x << ", " << y << ", " << z << std::endl;
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiEscort escortPackage(actorID, duration, x, y, z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||
|
||||
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
|
||||
<< std::endl;
|
||||
|
@ -147,7 +147,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiEscort escortPackage(actorID, cellID, duration, x, y, z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||
|
||||
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
|
||||
<< std::endl;
|
||||
|
@ -211,7 +211,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiWander wanderPackage(range, duration, time, idleList, repeat);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(wanderPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(wanderPackage, ptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -299,7 +299,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||
|
||||
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
|
||||
<< std::endl;
|
||||
|
@ -337,7 +337,7 @@ namespace MWScript
|
|||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
|
||||
<< std::endl;
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ namespace MWScript
|
|||
|
||||
creatureStats.setHostile(true);
|
||||
creatureStats.getAiSequence().stack(
|
||||
MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) ));
|
||||
MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) ), actor);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -388,5 +388,7 @@ op 0x200023c: StopCombat
|
|||
op 0x200023d: StopCombatExplicit
|
||||
op 0x200023e: GetPcInJail
|
||||
op 0x200023f: GetPcTraveling
|
||||
op 0x2000240: onKnockout
|
||||
op 0x2000241: onKnockoutExplicit
|
||||
|
||||
opcodes 0x2000240-0x3ffffff unused
|
||||
opcodes 0x2000242-0x3ffffff unused
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace MWScript
|
|||
return mScripts.size();
|
||||
}
|
||||
|
||||
void GlobalScripts::write (ESM::ESMWriter& writer) const
|
||||
void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
{
|
||||
for (std::map<std::string, std::pair<bool, Locals> >::const_iterator iter (mScripts.begin());
|
||||
iter!=mScripts.end(); ++iter)
|
||||
|
@ -113,6 +113,7 @@ namespace MWScript
|
|||
writer.startRecord (ESM::REC_GSCR);
|
||||
script.save (writer);
|
||||
writer.endRecord (ESM::REC_GSCR);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ namespace ESM
|
|||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct ESMStore;
|
||||
|
@ -46,7 +51,7 @@ namespace MWScript
|
|||
|
||||
int countSavedGameRecords() const;
|
||||
|
||||
void write (ESM::ESMWriter& writer) const;
|
||||
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
bool readRecord (ESM::ESMReader& reader, int32_t type);
|
||||
///< Records for variables that do not exist are dropped silently.
|
||||
|
|
|
@ -302,15 +302,23 @@ namespace MWScript
|
|||
std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first;
|
||||
|
||||
std::map<std::string, int> ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks();
|
||||
std::map<std::string, int>::const_iterator it = ranks.begin();
|
||||
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
|
||||
int rank = -1;
|
||||
if (it != ranks.end())
|
||||
rank = it->second;
|
||||
|
||||
// If you are not in the faction, PcRank returns the first rank, for whatever reason.
|
||||
// This is used by the dialogue when joining the Thieves Guild in Balmora.
|
||||
if (rank == -1)
|
||||
rank = 0;
|
||||
|
||||
const MWWorld::ESMStore &store = world->getStore();
|
||||
const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
|
||||
|
||||
if(it->second < 0 || it->second > 9) // there are only 10 ranks
|
||||
if(rank < 0 || rank > 9) // there are only 10 ranks
|
||||
return "";
|
||||
|
||||
return faction->mRanks[it->second];
|
||||
return faction->mRanks[rank];
|
||||
}
|
||||
|
||||
std::string InterpreterContext::getPCNextRank() const
|
||||
|
@ -320,25 +328,25 @@ namespace MWScript
|
|||
|
||||
std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first;
|
||||
|
||||
std::map<std::string, int> ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks();
|
||||
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
|
||||
int rank = -1;
|
||||
if (it != ranks.end())
|
||||
rank = it->second;
|
||||
|
||||
++rank; // Next rank
|
||||
|
||||
// if we are already at max rank, there is no next rank
|
||||
if (rank > 9)
|
||||
rank = 9;
|
||||
|
||||
const MWWorld::ESMStore &store = world->getStore();
|
||||
const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);
|
||||
|
||||
std::map<std::string, int> ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks();
|
||||
if(rank < 0 || rank > 9)
|
||||
return "";
|
||||
|
||||
if (!ranks.empty())
|
||||
{
|
||||
std::map<std::string, int>::const_iterator it = ranks.begin();
|
||||
|
||||
if(it->second < -1 || it->second > 9)
|
||||
return "";
|
||||
|
||||
if(it->second <= 8) // If player is at max rank, there is no next rank
|
||||
return faction->mRanks[it->second + 1];
|
||||
else
|
||||
return faction->mRanks[it->second];
|
||||
}
|
||||
else
|
||||
return faction->mRanks[0];
|
||||
return faction->mRanks[rank];
|
||||
}
|
||||
|
||||
int InterpreterContext::getPCBounty() const
|
||||
|
@ -370,10 +378,14 @@ namespace MWScript
|
|||
|
||||
float InterpreterContext::getDistance (const std::string& name, const std::string& id) const
|
||||
{
|
||||
// TODO handle exterior cells (when ref and ref2 are located in different cells)
|
||||
const MWWorld::Ptr ref2 = getReference (id, false);
|
||||
const MWWorld::Ptr ref2 = getReference (id, false, false);
|
||||
// If either actor is in a non-active cell, return a large value (just like vanilla)
|
||||
if (ref2.isEmpty())
|
||||
return std::numeric_limits<float>().max();
|
||||
|
||||
const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr (name, true);
|
||||
const MWWorld::Ptr ref = getReference (name, false, false);
|
||||
if (ref.isEmpty())
|
||||
return std::numeric_limits<float>().max();
|
||||
|
||||
double diff[3];
|
||||
|
||||
|
|
|
@ -1060,6 +1060,22 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnKnockout : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getKnockedDownOneFrame();
|
||||
|
||||
runtime.push (value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpIsWerewolf : public Interpreter::Opcode0
|
||||
{
|
||||
|
@ -1236,6 +1252,8 @@ namespace MWScript
|
|||
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
||||
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolf, new OpIsWerewolf<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeIsWerewolfExplicit, new OpIsWerewolf<ExplicitRef>);
|
||||
|
@ -1245,7 +1263,7 @@ namespace MWScript
|
|||
interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolf, new OpSetWerewolf<ImplicitRef, false>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolfExplicit, new OpSetWerewolf<ExplicitRef, false>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobatics, new OpSetWerewolfAcrobatics<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics<ExplicitRef>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -442,7 +442,7 @@ namespace MWSound
|
|||
{
|
||||
snditer->first->setFadeout(duration);
|
||||
}
|
||||
snditer++;
|
||||
++snditer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,21 @@ MWState::Character::Character (const boost::filesystem::path& saves, const std::
|
|||
}
|
||||
}
|
||||
|
||||
void MWState::Character::cleanup()
|
||||
{
|
||||
if (mSlots.size() == 0)
|
||||
{
|
||||
// All slots are gone, no need to keep the empty directory
|
||||
if (boost::filesystem::is_directory (mPath))
|
||||
{
|
||||
// Extra safety check to make sure the directory is empty (e.g. slots failed to parse header)
|
||||
boost::filesystem::directory_iterator it(mPath);
|
||||
if (it == boost::filesystem::directory_iterator())
|
||||
boost::filesystem::remove_all(mPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profile)
|
||||
{
|
||||
addSlot (profile);
|
||||
|
@ -102,6 +117,21 @@ const MWState::Slot *MWState::Character::createSlot (const ESM::SavedGame& profi
|
|||
return &mSlots.back();
|
||||
}
|
||||
|
||||
void MWState::Character::deleteSlot (const Slot *slot)
|
||||
{
|
||||
int index = slot - &mSlots[0];
|
||||
|
||||
if (index<0 || index>=static_cast<int> (mSlots.size()))
|
||||
{
|
||||
// sanity check; not entirely reliable
|
||||
throw std::logic_error ("slot not found");
|
||||
}
|
||||
|
||||
boost::filesystem::remove(slot->mPath);
|
||||
|
||||
mSlots.erase (mSlots.begin()+index);
|
||||
}
|
||||
|
||||
const MWState::Slot *MWState::Character::updateSlot (const Slot *slot, const ESM::SavedGame& profile)
|
||||
{
|
||||
int index = slot - &mSlots[0];
|
||||
|
@ -150,4 +180,4 @@ ESM::SavedGame MWState::Character::getSignature() const
|
|||
slot = *iter;
|
||||
|
||||
return slot.mProfile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,19 @@ namespace MWState
|
|||
|
||||
Character (const boost::filesystem::path& saves, const std::string& game);
|
||||
|
||||
void cleanup();
|
||||
///< Delete the directory we used, if it is empty
|
||||
|
||||
const Slot *createSlot (const ESM::SavedGame& profile);
|
||||
///< Create new slot.
|
||||
///
|
||||
/// \attention The ownership of the slot is not transferred.
|
||||
|
||||
/// \note Slot must belong to this character.
|
||||
///
|
||||
/// \attention The \a slot pointer will be invalidated by this call.
|
||||
void deleteSlot (const Slot *slot);
|
||||
|
||||
const Slot *updateSlot (const Slot *slot, const ESM::SavedGame& profile);
|
||||
/// \note Slot must belong to this character.
|
||||
///
|
||||
|
|
|
@ -47,6 +47,25 @@ MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create)
|
|||
return mCurrent;
|
||||
}
|
||||
|
||||
void MWState::CharacterManager::deleteSlot(const MWState::Character *character, const MWState::Slot *slot)
|
||||
{
|
||||
int index = character - &mCharacters[0];
|
||||
|
||||
if (index<0 || index>=static_cast<int> (mCharacters.size()))
|
||||
throw std::logic_error ("invalid character");
|
||||
|
||||
mCharacters[index].deleteSlot(slot);
|
||||
|
||||
if (mCharacters[index].begin() == mCharacters[index].end())
|
||||
{
|
||||
// All slots deleted, cleanup and remove this character
|
||||
mCharacters[index].cleanup();
|
||||
if (character == mCurrent)
|
||||
mCurrent = NULL;
|
||||
mCharacters.erase(mCharacters.begin() + index);
|
||||
}
|
||||
}
|
||||
|
||||
void MWState::CharacterManager::createCharacter()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace MWState
|
|||
Character *getCurrentCharacter (bool create = true);
|
||||
///< \param create Create a new character, if there is no current character.
|
||||
|
||||
void deleteSlot(const MWState::Character *character, const MWState::Slot *slot);
|
||||
|
||||
void createCharacter();
|
||||
///< Create new character within saved game management
|
||||
|
||||
|
|
|
@ -196,26 +196,36 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0
|
||||
|
||||
writer.setFormat (ESM::Header::CurrentFormat);
|
||||
writer.setRecordCount (
|
||||
1 // saved game header
|
||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||
+MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
|
||||
+1 // global map
|
||||
);
|
||||
int recordCount = 1 // saved game header
|
||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||
+MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWindowManager()->countSavedGameRecords();
|
||||
writer.setRecordCount (recordCount);
|
||||
|
||||
writer.save (stream);
|
||||
|
||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
listener.setProgressRange(recordCount);
|
||||
listener.setLabel("#{sNotifyMessage4}");
|
||||
|
||||
Loading::ScopedLoad load(&listener);
|
||||
|
||||
writer.startRecord (ESM::REC_SAVE);
|
||||
slot->mProfile.save (writer);
|
||||
writer.endRecord (ESM::REC_SAVE);
|
||||
listener.increaseProgress();
|
||||
|
||||
MWBase::Environment::get().getJournal()->write (writer);
|
||||
MWBase::Environment::get().getDialogueManager()->write (writer);
|
||||
MWBase::Environment::get().getWorld()->write (writer);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer);
|
||||
MWBase::Environment::get().getWindowManager()->write(writer);
|
||||
MWBase::Environment::get().getJournal()->write (writer, listener);
|
||||
MWBase::Environment::get().getDialogueManager()->write (writer, listener);
|
||||
MWBase::Environment::get().getWorld()->write (writer, listener);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener);
|
||||
MWBase::Environment::get().getWindowManager()->write(writer, listener);
|
||||
|
||||
// Ensure we have written the number of records that was estimated
|
||||
if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record
|
||||
std::cerr << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount() << std::endl;
|
||||
|
||||
writer.close();
|
||||
|
||||
|
@ -225,8 +235,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
|
||||
void MWState::StateManager::quickSave (std::string name)
|
||||
{
|
||||
if (mState!=State_Running ||
|
||||
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")!=-1) // char gen
|
||||
if (!(mState==State_Running &&
|
||||
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 // char gen
|
||||
&& MWBase::Environment::get().getWindowManager()->isSavingAllowed()))
|
||||
{
|
||||
//You can not save your game right now
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sSaveGameDenied}");
|
||||
|
@ -243,8 +254,6 @@ void MWState::StateManager::quickSave (std::string name)
|
|||
slot = &*it;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage4}");
|
||||
|
||||
saveGame(name, slot);
|
||||
}
|
||||
|
||||
|
@ -261,6 +270,13 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
|
||||
std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);
|
||||
|
||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
|
||||
listener.setProgressRange(reader.getRecordCount());
|
||||
listener.setLabel("#{sLoadingMessage14}");
|
||||
|
||||
Loading::ScopedLoad load(&listener);
|
||||
|
||||
while (reader.hasMoreRecs())
|
||||
{
|
||||
ESM::NAME n = reader.getRecName();
|
||||
|
@ -308,7 +324,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
break;
|
||||
|
||||
case ESM::REC_GMAP:
|
||||
|
||||
case ESM::REC_KEYS:
|
||||
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
|
||||
break;
|
||||
|
||||
|
@ -318,6 +334,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
/// \todo log error
|
||||
reader.skipRecord();
|
||||
}
|
||||
listener.increaseProgress();
|
||||
}
|
||||
|
||||
mCharacterManager.setCurrentCharacter(character);
|
||||
|
@ -350,10 +367,12 @@ void MWState::StateManager::quickLoad()
|
|||
{
|
||||
if (Character* mCurrentCharacter = getCurrentCharacter (false))
|
||||
if (const MWState::Slot* slot = &*mCurrentCharacter->begin()) //Get newest save
|
||||
{
|
||||
//MWBase::Environment::get().getWindowManager()->messageBox("#{sLoadingMessage14}"); //it overlaps
|
||||
loadGame (mCurrentCharacter, slot);
|
||||
}
|
||||
}
|
||||
|
||||
void MWState::StateManager::deleteGame(const MWState::Character *character, const MWState::Slot *slot)
|
||||
{
|
||||
mCharacterManager.deleteSlot(character, slot);
|
||||
}
|
||||
|
||||
MWState::Character *MWState::StateManager::getCurrentCharacter (bool create)
|
||||
|
|
|
@ -44,6 +44,9 @@ namespace MWState
|
|||
|
||||
virtual void endGame();
|
||||
|
||||
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot);
|
||||
///< Delete a saved game slot from this character. If all save slots are deleted, the character will be deleted too.
|
||||
|
||||
virtual void saveGame (const std::string& description, const Slot *slot = 0);
|
||||
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
|
||||
///
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
|
@ -24,7 +23,12 @@ namespace MWWorld
|
|||
|
||||
void ActionRead::executeImp (const MWWorld::Ptr& actor) {
|
||||
|
||||
if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { //Ensure we're not in combat
|
||||
//Ensure we're not in combat
|
||||
if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()
|
||||
// Reading in combat is still allowed if the scroll/book is not in the player inventory yet
|
||||
// (since otherwise, there would be no way to pick it up)
|
||||
&& getTarget().getContainerStore() == &actor.getClass().getContainerStore(actor)
|
||||
) {
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage4}");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace MWWorld
|
|||
|
||||
//find any NPC that is following the actor and teleport him too
|
||||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
|
||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();it++)
|
||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||
{
|
||||
std::cout << "teleporting someone!" << (*it).getCellRef().mRefID;
|
||||
executeImp(*it);
|
||||
|
|
|
@ -277,17 +277,23 @@ int MWWorld::Cells::countSavedGameRecords() const
|
|||
return count;
|
||||
}
|
||||
|
||||
void MWWorld::Cells::write (ESM::ESMWriter& writer) const
|
||||
void MWWorld::Cells::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
{
|
||||
for (std::map<std::pair<int, int>, CellStore>::iterator iter (mExteriors.begin());
|
||||
iter!=mExteriors.end(); ++iter)
|
||||
if (iter->second.hasState())
|
||||
{
|
||||
writeCell (writer, iter->second);
|
||||
progress.increaseProgress(); // Assumes that each cell writes one record
|
||||
}
|
||||
|
||||
for (std::map<std::string, CellStore>::iterator iter (mInteriors.begin());
|
||||
iter!=mInteriors.end(); ++iter)
|
||||
if (iter->second.hasState())
|
||||
{
|
||||
writeCell (writer, iter->second);
|
||||
progress.increaseProgress(); // Assumes that each cell writes one record
|
||||
}
|
||||
}
|
||||
|
||||
bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
|
|
|
@ -15,6 +15,11 @@ namespace ESM
|
|||
struct Cell;
|
||||
}
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ESMStore;
|
||||
|
@ -69,7 +74,7 @@ namespace MWWorld
|
|||
|
||||
int countSavedGameRecords() const;
|
||||
|
||||
void write (ESM::ESMWriter& writer) const;
|
||||
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||
|
||||
bool readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
const std::map<int, int>& contentFileMap);
|
||||
|
|
|
@ -433,7 +433,6 @@ namespace MWWorld
|
|||
while(mCell->getNextRef(esm[index], ref, deleted))
|
||||
{
|
||||
// Don't load reference if it was moved to a different cell.
|
||||
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
|
||||
ESM::MovedCellRefTracker::const_iterator iter =
|
||||
std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum);
|
||||
if (iter != mCell->mMovedRefs.end()) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue