forked from mirror/openmw-tes3mp
Merge remote branch 'upstream/master'
commit
b945ce61a5
@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
#include "journal.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/environment.hpp"
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
Quest& Journal::getQuest (const std::string& id)
|
||||||
|
{
|
||||||
|
TQuestContainer::iterator iter = mQuests.find (id);
|
||||||
|
|
||||||
|
if (iter==mQuests.end())
|
||||||
|
{
|
||||||
|
std::pair<TQuestContainer::iterator, bool> result =
|
||||||
|
mQuests.insert (std::make_pair (id, Quest (id)));
|
||||||
|
|
||||||
|
iter = result.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::Journal (MWWorld::Environment& environment)
|
||||||
|
: mEnvironment (environment)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Journal::addEntry (const std::string& id, int index)
|
||||||
|
{
|
||||||
|
StampedJournalEntry entry =
|
||||||
|
StampedJournalEntry::makeFromQuest (id, index, *mEnvironment.mWorld);
|
||||||
|
|
||||||
|
mJournal.push_back (entry);
|
||||||
|
|
||||||
|
Quest& quest = getQuest (id);
|
||||||
|
|
||||||
|
quest.addEntry (entry, *mEnvironment.mWorld); // we are doing slicing on purpose here
|
||||||
|
}
|
||||||
|
|
||||||
|
void Journal::setJournalIndex (const std::string& id, int index)
|
||||||
|
{
|
||||||
|
Quest& quest = getQuest (id);
|
||||||
|
|
||||||
|
quest.setIndex (index, *mEnvironment.mWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Journal::addTopic (const std::string& topicId, const std::string& infoId)
|
||||||
|
{
|
||||||
|
TTopicContainer::iterator iter = mTopics.find (topicId);
|
||||||
|
|
||||||
|
if (iter==mTopics.end())
|
||||||
|
{
|
||||||
|
std::pair<TTopicContainer::iterator, bool> result
|
||||||
|
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
|
||||||
|
|
||||||
|
iter = result.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->second.addEntry (JournalEntry (topicId, infoId), *mEnvironment.mWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Journal::getJournalIndex (const std::string& id) const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TEntryIter Journal::begin() const
|
||||||
|
{
|
||||||
|
return mJournal.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TEntryIter Journal::end() const
|
||||||
|
{
|
||||||
|
return mJournal.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TQuestIter Journal::questBegin() const
|
||||||
|
{
|
||||||
|
return mQuests.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TQuestIter Journal::questEnd() const
|
||||||
|
{
|
||||||
|
return mQuests.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TTopicIter Journal::topicBegin() const
|
||||||
|
{
|
||||||
|
return mTopics.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
Journal::TTopicIter Journal::topicEnd() const
|
||||||
|
{
|
||||||
|
return mTopics.end();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef GAME_MMDIALOG_JOURNAL_H
|
||||||
|
#define GAME_MWDIALOG_JOURNAL_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "journalentry.hpp"
|
||||||
|
#include "quest.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
struct Environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
/// \brief The player's journal
|
||||||
|
class Journal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef std::deque<StampedJournalEntry> TEntryContainer;
|
||||||
|
typedef TEntryContainer::const_iterator TEntryIter;
|
||||||
|
typedef std::map<std::string, Quest> TQuestContainer; // topc, quest
|
||||||
|
typedef TQuestContainer::const_iterator TQuestIter;
|
||||||
|
typedef std::map<std::string, Topic> TTopicContainer; // topic-id, topic-content
|
||||||
|
typedef TTopicContainer::const_iterator TTopicIter;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
MWWorld::Environment& mEnvironment;
|
||||||
|
TEntryContainer mJournal;
|
||||||
|
TQuestContainer mQuests;
|
||||||
|
TTopicContainer mTopics;
|
||||||
|
|
||||||
|
Quest& getQuest (const std::string& id);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Journal (MWWorld::Environment& environment);
|
||||||
|
|
||||||
|
void addEntry (const std::string& id, int index);
|
||||||
|
///< Add a journal entry.
|
||||||
|
|
||||||
|
void setJournalIndex (const std::string& id, int index);
|
||||||
|
///< Set the journal index without adding an entry.
|
||||||
|
|
||||||
|
int getJournalIndex (const std::string& id) const;
|
||||||
|
///< Get the journal index.
|
||||||
|
|
||||||
|
void addTopic (const std::string& topicId, const std::string& infoId);
|
||||||
|
|
||||||
|
TEntryIter begin() const;
|
||||||
|
///< Iterator pointing to the begin of the main journal.
|
||||||
|
///
|
||||||
|
/// \note Iterators to main journal entries will never become invalid.
|
||||||
|
|
||||||
|
TEntryIter end() const;
|
||||||
|
///< Iterator pointing past the end of the main journal.
|
||||||
|
|
||||||
|
TQuestIter questBegin() const;
|
||||||
|
///< Iterator pointing to the first quest (sorted by topic ID)
|
||||||
|
|
||||||
|
TQuestIter questEnd() const;
|
||||||
|
///< Iterator pointing past the last quest.
|
||||||
|
|
||||||
|
TTopicIter topicBegin() const;
|
||||||
|
///< Iterator pointing to the first topic (sorted by topic ID)
|
||||||
|
///
|
||||||
|
/// \note The topic ID is identical with the user-visible topic string.
|
||||||
|
|
||||||
|
TTopicIter topicEnd() const;
|
||||||
|
///< Iterator pointing past the last topic.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
#include "journalentry.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <components/esm_store/store.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/world.hpp"
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
JournalEntry::JournalEntry() {}
|
||||||
|
|
||||||
|
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
|
||||||
|
: mTopic (topic), mInfoId (infoId)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string JournalEntry::getText (const ESMS::ESMStore& store) const
|
||||||
|
{
|
||||||
|
const ESM::Dialogue *dialogue = store.dialogs.find (mTopic);
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
if (iter->id==mInfoId)
|
||||||
|
return iter->response;
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
return JournalEntry (topic, idFromIndex (topic, index, world));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JournalEntry::idFromIndex (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (topic);
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
if (iter->data.disposition==index) /// \todo cleanup info structure
|
||||||
|
{
|
||||||
|
iter->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown journal index for topic " + topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
StampedJournalEntry::StampedJournalEntry()
|
||||||
|
: mDay (0), mMonth (0), mDayOfMonth (0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,
|
||||||
|
int day, int month, int dayOfMonth)
|
||||||
|
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
int day = world.getGlobalVariable ("dayspassed").mLong;
|
||||||
|
int month = world.getGlobalVariable ("day").mLong;
|
||||||
|
int dayOfMonth = world.getGlobalVariable ("month").mLong;
|
||||||
|
|
||||||
|
return StampedJournalEntry (topic, idFromIndex (topic, index, world), day, month, dayOfMonth);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef GAME_MMDIALOGUE_JOURNALENTRY_H
|
||||||
|
#define GAME_MMDIALOGUE_JOURNALENTRY_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ESMS
|
||||||
|
{
|
||||||
|
struct ESMStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class World;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
/// \brief A quest or dialogue entry
|
||||||
|
struct JournalEntry
|
||||||
|
{
|
||||||
|
std::string mTopic;
|
||||||
|
std::string mInfoId;
|
||||||
|
|
||||||
|
JournalEntry();
|
||||||
|
|
||||||
|
JournalEntry (const std::string& topic, const std::string& infoId);
|
||||||
|
|
||||||
|
std::string getText (const ESMS::ESMStore& store) const;
|
||||||
|
|
||||||
|
static JournalEntry makeFromQuest (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world);
|
||||||
|
|
||||||
|
static std::string idFromIndex (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \biref A quest entry with a timestamp.
|
||||||
|
struct StampedJournalEntry : public JournalEntry
|
||||||
|
{
|
||||||
|
int mDay;
|
||||||
|
int mMonth;
|
||||||
|
int mDayOfMonth;
|
||||||
|
|
||||||
|
StampedJournalEntry();
|
||||||
|
|
||||||
|
StampedJournalEntry (const std::string& topic, const std::string& infoId,
|
||||||
|
int day, int month, int dayOfMonth);
|
||||||
|
|
||||||
|
static StampedJournalEntry makeFromQuest (const std::string& topic, int index,
|
||||||
|
const MWWorld::World& world);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
#include "quest.hpp"
|
||||||
|
|
||||||
|
#include <components/esm_store/store.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/world.hpp"
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
Quest::Quest()
|
||||||
|
: Topic(), mIndex (0), mFinished (false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Quest::Quest (const std::string& topic)
|
||||||
|
: Topic (topic), mIndex (0), mFinished (false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string Quest::getName (const MWWorld::World& world) const
|
||||||
|
{
|
||||||
|
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
if (iter->questStatus==ESM::DialInfo::QS_Name)
|
||||||
|
return iter->response;
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int Quest::getIndex() const
|
||||||
|
{
|
||||||
|
return mIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quest::setIndex (int index, const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
if (iter->data.disposition==index && iter->questStatus!=ESM::DialInfo::QS_Name)
|
||||||
|
{
|
||||||
|
mIndex = index;
|
||||||
|
|
||||||
|
if (iter->questStatus==ESM::DialInfo::QS_Finished)
|
||||||
|
mFinished = true;
|
||||||
|
else if (iter->questStatus==ESM::DialInfo::QS_Restart)
|
||||||
|
mFinished = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error ("unknown journal index for topic " + mTopic);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Quest::isFinished() const
|
||||||
|
{
|
||||||
|
return mFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quest::addEntry (const JournalEntry& entry, const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (entry.mTopic);
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||||
|
iter!=dialogue->mInfo.end(); ++iter)
|
||||||
|
if (iter->id==entry.mInfoId)
|
||||||
|
{
|
||||||
|
index = iter->data.disposition; /// \todo cleanup info structure
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
throw std::runtime_error ("unknown journal entry for topic " + mTopic);
|
||||||
|
|
||||||
|
setIndex (index, world);
|
||||||
|
|
||||||
|
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
|
||||||
|
if (*iter==entry.mInfoId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mEntries.push_back (entry.mInfoId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef GAME_MMDIALOG_QUEST_H
|
||||||
|
#define GAME_MWDIALOG_QUEST_H
|
||||||
|
|
||||||
|
#include "topic.hpp"
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
/// \brief A quest in progress or a compelted quest
|
||||||
|
class Quest : public Topic
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
bool mFinished;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Quest();
|
||||||
|
|
||||||
|
Quest (const std::string& topic);
|
||||||
|
|
||||||
|
const std::string getName (const MWWorld::World& world) const;
|
||||||
|
///< May be an empty string
|
||||||
|
|
||||||
|
int getIndex() const;
|
||||||
|
|
||||||
|
void setIndex (int index, const MWWorld::World& world);
|
||||||
|
///< Calling this function with a non-existant index while throw an exception.
|
||||||
|
|
||||||
|
bool isFinished() const;
|
||||||
|
|
||||||
|
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
|
||||||
|
///< Add entry and adjust index accordingly.
|
||||||
|
///
|
||||||
|
/// \note Redundant entries are ignored, but the index is still adjusted.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
#include "topic.hpp"
|
||||||
|
|
||||||
|
#include <components/esm_store/store.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/world.hpp"
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
Topic::Topic()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Topic::Topic (const std::string& topic)
|
||||||
|
: mTopic (topic)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Topic::~Topic()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Topic::addEntry (const JournalEntry& entry, const MWWorld::World& world)
|
||||||
|
{
|
||||||
|
if (entry.mTopic!=mTopic)
|
||||||
|
throw std::runtime_error ("topic does not match: " + mTopic);
|
||||||
|
|
||||||
|
for (TEntryIter iter = begin(); iter!=end(); ++iter)
|
||||||
|
if (*iter==entry.mInfoId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mEntries.push_back (entry.mInfoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Topic::TEntryIter Topic::begin()
|
||||||
|
{
|
||||||
|
return mEntries.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
Topic::TEntryIter Topic::end()
|
||||||
|
{
|
||||||
|
return mEntries.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
JournalEntry Topic::getEntry (const std::string& infoId)
|
||||||
|
{
|
||||||
|
return JournalEntry (mTopic, infoId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef GAME_MMDIALOG_TOPIC_H
|
||||||
|
#define GAME_MWDIALOG_TOPIC_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "journalentry.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class World;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWDialogue
|
||||||
|
{
|
||||||
|
/// \brief Collection of seen responses for a topic
|
||||||
|
class Topic
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef std::vector<std::string> TEntryContainer;
|
||||||
|
typedef TEntryContainer::const_iterator TEntryIter;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
std::string mTopic;
|
||||||
|
TEntryContainer mEntries; // info-IDs
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Topic();
|
||||||
|
|
||||||
|
Topic (const std::string& topic);
|
||||||
|
|
||||||
|
virtual ~Topic();
|
||||||
|
|
||||||
|
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
|
||||||
|
///< Add entry
|
||||||
|
///
|
||||||
|
/// \note Redundant entries are ignored.
|
||||||
|
|
||||||
|
TEntryIter begin();
|
||||||
|
///< Iterator pointing to the begin of the journal for this topic.
|
||||||
|
|
||||||
|
TEntryIter end();
|
||||||
|
///< Iterator pointing past the end of the journal for this topic.
|
||||||
|
|
||||||
|
JournalEntry getEntry (const std::string& infoId);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
#include "dialogueextensions.hpp"
|
||||||
|
|
||||||
|
#include <components/compiler/extensions.hpp>
|
||||||
|
|
||||||
|
#include <components/interpreter/interpreter.hpp>
|
||||||
|
#include <components/interpreter/runtime.hpp>
|
||||||
|
#include <components/interpreter/opcodes.hpp>
|
||||||
|
|
||||||
|
#include "../mwdialogue/journal.hpp"
|
||||||
|
|
||||||
|
#include "interpretercontext.hpp"
|
||||||
|
|
||||||
|
namespace MWScript
|
||||||
|
{
|
||||||
|
namespace Dialogue
|
||||||
|
{
|
||||||
|
class OpJournal : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
Interpreter::Type_Integer index = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
context.getEnvironment().mJournal->addEntry (quest, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpSetJournalIndex : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
Interpreter::Type_Integer index = runtime[0].mInteger;
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
context.getEnvironment().mJournal->setJournalIndex (quest, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpGetJournalIndex : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext& context
|
||||||
|
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
int index = context.getEnvironment().mJournal->getJournalIndex (quest);
|
||||||
|
|
||||||
|
runtime.push (index);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const int opcodeJournal = 0x2000133;
|
||||||
|
const int opcodeSetJournalIndex = 0x2000134;
|
||||||
|
const int opcodeGetJournalIndex = 0x2000135;
|
||||||
|
|
||||||
|
void registerExtensions (Compiler::Extensions& extensions)
|
||||||
|
{
|
||||||
|
extensions.registerInstruction ("journal", "cl", opcodeJournal);
|
||||||
|
extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
|
||||||
|
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||||
|
{
|
||||||
|
interpreter.installSegment5 (opcodeJournal, new OpJournal);
|
||||||
|
interpreter.installSegment5 (opcodeSetJournalIndex, new OpSetJournalIndex);
|
||||||
|
interpreter.installSegment5 (opcodeGetJournalIndex, new OpGetJournalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef GAME_SCRIPT_DIALOGUEEXTENSIONS_H
|
||||||
|
#define GAME_SCRIPT_DIALOGUEEXTENSIONS_H
|
||||||
|
|
||||||
|
namespace Compiler
|
||||||
|
{
|
||||||
|
class Extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Interpreter
|
||||||
|
{
|
||||||
|
class Interpreter;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWScript
|
||||||
|
{
|
||||||
|
/// \brief Dialogue/Journal-related script functionality
|
||||||
|
namespace Dialogue
|
||||||
|
{
|
||||||
|
void registerExtensions (Compiler::Extensions& extensions);
|
||||||
|
|
||||||
|
void installOpcodes (Interpreter::Interpreter& interpreter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef PATH__HPP
|
|
||||||
#define PATH__HPP
|
|
||||||
|
|
||||||
#include <OgrePlatform.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
|
||||||
#include <OSX/macUtils.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace OMW
|
|
||||||
{
|
|
||||||
class Path
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum PathTypeEnum
|
|
||||||
{
|
|
||||||
USER_CFG_PATH,
|
|
||||||
GLOBAL_CFG_PATH
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::string getPath(PathTypeEnum parType, const std::string parApp, const std::string parFile);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -0,0 +1,345 @@
|
|||||||
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
ESM_Context ESMReader::getContext()
|
||||||
|
{
|
||||||
|
// Update the file position before returning
|
||||||
|
mCtx.filePos = mEsm->tell();
|
||||||
|
return mCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::restoreContext(const ESM_Context &rc)
|
||||||
|
{
|
||||||
|
// Reopen the file if necessary
|
||||||
|
if (mCtx.filename != rc.filename)
|
||||||
|
openRaw(rc.filename);
|
||||||
|
|
||||||
|
// Copy the data
|
||||||
|
mCtx = rc;
|
||||||
|
|
||||||
|
// Make sure we seek to the right place
|
||||||
|
mEsm->seek(mCtx.filePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::close()
|
||||||
|
{
|
||||||
|
mEsm.reset();
|
||||||
|
mCtx.filename.clear();
|
||||||
|
mCtx.leftFile = 0;
|
||||||
|
mCtx.leftRec = 0;
|
||||||
|
mCtx.leftSub = 0;
|
||||||
|
mCtx.subCached = false;
|
||||||
|
mCtx.recName.val = 0;
|
||||||
|
mCtx.subName.val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
mEsm = _esm;
|
||||||
|
mCtx.filename = name;
|
||||||
|
mCtx.leftFile = mEsm->size();
|
||||||
|
|
||||||
|
// Flag certain files for special treatment, based on the file
|
||||||
|
// name.
|
||||||
|
const char *cstr = mCtx.filename.c_str();
|
||||||
|
if (iends(cstr, "Morrowind.esm"))
|
||||||
|
mSpf = SF_Morrowind;
|
||||||
|
else if (iends(cstr, "Tribunal.esm"))
|
||||||
|
mSpf = SF_Tribunal;
|
||||||
|
else if (iends(cstr, "Bloodmoon.esm"))
|
||||||
|
mSpf = SF_Bloodmoon;
|
||||||
|
else
|
||||||
|
mSpf = SF_Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||||
|
{
|
||||||
|
openRaw(_esm, name);
|
||||||
|
|
||||||
|
if (getRecName() != "TES3")
|
||||||
|
fail("Not a valid Morrowind file");
|
||||||
|
|
||||||
|
getRecHeader();
|
||||||
|
|
||||||
|
// Get the header
|
||||||
|
getHNT(mCtx.header, "HEDR", 300);
|
||||||
|
|
||||||
|
if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13)
|
||||||
|
fail("Unsupported file format version");
|
||||||
|
|
||||||
|
while (isNextSub("MAST"))
|
||||||
|
{
|
||||||
|
MasterData m;
|
||||||
|
m.name = getHString();
|
||||||
|
m.size = getHNLong("DATA");
|
||||||
|
mMasters.push_back(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCtx.header.type == FT_ESS)
|
||||||
|
{
|
||||||
|
// Savegame-related data
|
||||||
|
|
||||||
|
// Player position etc
|
||||||
|
getHNT(mSaveData, "GMDT", 124);
|
||||||
|
|
||||||
|
/* Image properties, five ints. Is always:
|
||||||
|
Red-mask: 0xff0000
|
||||||
|
Blue-mask: 0x00ff00
|
||||||
|
Green-mask: 0x0000ff
|
||||||
|
Alpha-mask: 0x000000
|
||||||
|
Bpp: 32
|
||||||
|
*/
|
||||||
|
getSubNameIs("SCRD");
|
||||||
|
skipHSubSize(20);
|
||||||
|
|
||||||
|
/* Savegame screenshot:
|
||||||
|
128x128 pixels * 4 bytes per pixel
|
||||||
|
*/
|
||||||
|
getSubNameIs("SCRS");
|
||||||
|
skipHSubSize(65536);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::open(const std::string &file)
|
||||||
|
{
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
open(StreamPtr(new FileStream(file)), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::openRaw(const std::string &file)
|
||||||
|
{
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
openRaw(StreamPtr(new FileStream(file)), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ESMReader::getHNLong(const char *name)
|
||||||
|
{
|
||||||
|
int64_t val;
|
||||||
|
getHNT(val, name);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHNOString(const char* name)
|
||||||
|
{
|
||||||
|
if (isNextSub(name))
|
||||||
|
return getHString();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHNString(const char* name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
return getHString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHString()
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
|
||||||
|
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||||
|
// occur in any of the official mods, but MultiMark makes use of
|
||||||
|
// them. For some reason, they break the rules, and contain a byte
|
||||||
|
// (value 0) even if the header says there is no data. If
|
||||||
|
// Morrowind accepts it, so should we.
|
||||||
|
if (mCtx.leftSub == 0)
|
||||||
|
{
|
||||||
|
// Skip the following zero byte
|
||||||
|
mCtx.leftRec--;
|
||||||
|
char c;
|
||||||
|
mEsm->read(&c, 1);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return getString(mCtx.leftSub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getHExact(void*p, int size)
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
if (size != static_cast<int> (mCtx.leftSub))
|
||||||
|
fail("getHExact() size mismatch");
|
||||||
|
getExact(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the given number of bytes from a named subrecord
|
||||||
|
void ESMReader::getHNExact(void*p, int size, const char* name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
getHExact(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next subrecord name and check if it matches the parameter
|
||||||
|
void ESMReader::getSubNameIs(const char* name)
|
||||||
|
{
|
||||||
|
getSubName();
|
||||||
|
if (mCtx.subName != name)
|
||||||
|
fail(
|
||||||
|
"Expected subrecord " + std::string(name) + " but got "
|
||||||
|
+ mCtx.subName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESMReader::isNextSub(const char* name)
|
||||||
|
{
|
||||||
|
if (!mCtx.leftRec)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
getSubName();
|
||||||
|
|
||||||
|
// If the name didn't match, then mark the it as 'cached' so it's
|
||||||
|
// available for the next call to getSubName.
|
||||||
|
mCtx.subCached = (mCtx.subName != name);
|
||||||
|
|
||||||
|
// If subCached is false, then subName == name.
|
||||||
|
return !mCtx.subCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||||
|
// slightly.
|
||||||
|
void ESMReader::getSubName()
|
||||||
|
{
|
||||||
|
// If the name has already been read, do nothing
|
||||||
|
if (mCtx.subCached)
|
||||||
|
{
|
||||||
|
mCtx.subCached = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reading the subrecord data anyway.
|
||||||
|
mEsm->read(mCtx.subName.name, 4);
|
||||||
|
mCtx.leftRec -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESMReader::isEmptyOrGetName()
|
||||||
|
{
|
||||||
|
if (mCtx.leftRec)
|
||||||
|
{
|
||||||
|
mEsm->read(mCtx.subName.name, 4);
|
||||||
|
mCtx.leftRec -= 4;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHSub()
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
skip(mCtx.leftSub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHSubSize(int size)
|
||||||
|
{
|
||||||
|
skipHSub();
|
||||||
|
if (static_cast<int> (mCtx.leftSub) != size)
|
||||||
|
fail("skipHSubSize() mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getSubHeader()
|
||||||
|
{
|
||||||
|
if (mCtx.leftRec < 4)
|
||||||
|
fail("End of record while reading sub-record header");
|
||||||
|
|
||||||
|
// Get subrecord size
|
||||||
|
getT(mCtx.leftSub);
|
||||||
|
|
||||||
|
// Adjust number of record bytes left
|
||||||
|
mCtx.leftRec -= mCtx.leftSub + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getSubHeaderIs(int size)
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
if (size != static_cast<int> (mCtx.leftSub))
|
||||||
|
fail("getSubHeaderIs(): Sub header mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
NAME ESMReader::getRecName()
|
||||||
|
{
|
||||||
|
if (!hasMoreRecs())
|
||||||
|
fail("No more records, getRecName() failed");
|
||||||
|
getName(mCtx.recName);
|
||||||
|
mCtx.leftFile -= 4;
|
||||||
|
|
||||||
|
// Make sure we don't carry over any old cached subrecord
|
||||||
|
// names. This can happen in some cases when we skip parts of a
|
||||||
|
// record.
|
||||||
|
mCtx.subCached = false;
|
||||||
|
|
||||||
|
return mCtx.recName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipRecord()
|
||||||
|
{
|
||||||
|
skip(mCtx.leftRec);
|
||||||
|
mCtx.leftRec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHRecord()
|
||||||
|
{
|
||||||
|
if (!mCtx.leftFile)
|
||||||
|
return;
|
||||||
|
getRecHeader();
|
||||||
|
skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getRecHeader(uint32_t &flags)
|
||||||
|
{
|
||||||
|
// General error checking
|
||||||
|
if (mCtx.leftFile < 12)
|
||||||
|
fail("End of file while reading record header");
|
||||||
|
if (mCtx.leftRec)
|
||||||
|
fail("Previous record contains unread bytes");
|
||||||
|
|
||||||
|
getUint(mCtx.leftRec);
|
||||||
|
getUint(flags);// This header entry is always zero
|
||||||
|
getUint(flags);
|
||||||
|
mCtx.leftFile -= 12;
|
||||||
|
|
||||||
|
// Check that sizes add up
|
||||||
|
if (mCtx.leftFile < mCtx.leftRec)
|
||||||
|
fail("Record size is larger than rest of file");
|
||||||
|
|
||||||
|
// Adjust number of bytes mCtx.left in file
|
||||||
|
mCtx.leftFile -= mCtx.leftRec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* Lowest level data reading and misc methods
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
void ESMReader::getExact(void*x, int size)
|
||||||
|
{
|
||||||
|
int t = mEsm->read(x, size);
|
||||||
|
if (t != size)
|
||||||
|
fail("Read error");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getString(int size)
|
||||||
|
{
|
||||||
|
char *ptr = ToUTF8::getBuffer(size);
|
||||||
|
mEsm->read(ptr, size);
|
||||||
|
|
||||||
|
// Convert to UTF8 and return
|
||||||
|
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::fail(const std::string &msg)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
|
||||||
|
ss << "ESM Error: " << msg;
|
||||||
|
ss << "\n File: " << mCtx.filename;
|
||||||
|
ss << "\n Record: " << mCtx.recName.toString();
|
||||||
|
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||||
|
if (mEsm != NULL)
|
||||||
|
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
||||||
|
throw std::runtime_error(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,175 +0,0 @@
|
|||||||
#include "records.hpp"
|
|
||||||
|
|
||||||
/** Implementation for some of the load() functions. Most are found in
|
|
||||||
the header files themselves, but this is a bit irritating to
|
|
||||||
compile if you're changing the functions often, as virtually the
|
|
||||||
entire engine depends on these headers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace ESM
|
|
||||||
{
|
|
||||||
void NPC::load(ESMReader &esm, const std::string& id)
|
|
||||||
{
|
|
||||||
mId = id;
|
|
||||||
|
|
||||||
npdt52.gold = -10;
|
|
||||||
|
|
||||||
model = esm.getHNOString("MODL");
|
|
||||||
name = esm.getHNOString("FNAM");
|
|
||||||
|
|
||||||
race = esm.getHNString("RNAM");
|
|
||||||
cls = esm.getHNString("CNAM");
|
|
||||||
faction = esm.getHNString("ANAM");
|
|
||||||
head = esm.getHNString("BNAM");
|
|
||||||
hair = esm.getHNString("KNAM");
|
|
||||||
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
|
|
||||||
esm.getSubNameIs("NPDT");
|
|
||||||
esm.getSubHeader();
|
|
||||||
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
|
|
||||||
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
|
|
||||||
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
|
|
||||||
|
|
||||||
esm.getHNT(flags, "FLAG");
|
|
||||||
|
|
||||||
inventory.load(esm);
|
|
||||||
spells.load(esm);
|
|
||||||
|
|
||||||
if(esm.isNextSub("AIDT"))
|
|
||||||
{
|
|
||||||
esm.getHExact(&AI, sizeof(AI));
|
|
||||||
hasAI = true;
|
|
||||||
}
|
|
||||||
else hasAI = false;
|
|
||||||
|
|
||||||
esm.skipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialInfo::load(ESMReader &esm)
|
|
||||||
{
|
|
||||||
id = esm.getHNString("INAM");
|
|
||||||
prev = esm.getHNString("PNAM");
|
|
||||||
next = esm.getHNString("NNAM");
|
|
||||||
|
|
||||||
// Not present if deleted
|
|
||||||
if(esm.isNextSub("DATA"))
|
|
||||||
esm.getHT(data, 12);
|
|
||||||
|
|
||||||
// What follows is somewhat spaghetti-ish, but it's worth if for
|
|
||||||
// an extra speedup. INFO is by far the most common record type.
|
|
||||||
|
|
||||||
// subName is a reference to the original, so it changes whenever
|
|
||||||
// a new sub name is read. esm.isEmptyOrGetName() will get the
|
|
||||||
// next name for us, or return true if there are no more records.
|
|
||||||
esm.getSubName();
|
|
||||||
const NAME &subName = esm.retSubName();
|
|
||||||
|
|
||||||
if(subName.val == REC_ONAM)
|
|
||||||
{
|
|
||||||
actor = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_RNAM)
|
|
||||||
{
|
|
||||||
race = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_CNAM)
|
|
||||||
{
|
|
||||||
clas = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
factionLess = false;
|
|
||||||
if(subName.val == REC_FNAM)
|
|
||||||
{
|
|
||||||
npcFaction = esm.getHString();
|
|
||||||
if(npcFaction == "FFFF") factionLess = true;
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_ANAM)
|
|
||||||
{
|
|
||||||
cell = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_DNAM)
|
|
||||||
{
|
|
||||||
pcFaction = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_SNAM)
|
|
||||||
{
|
|
||||||
sound = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
if(subName.val == REC_NAME)
|
|
||||||
{
|
|
||||||
response = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(subName.val == REC_SCVR)
|
|
||||||
{
|
|
||||||
SelectStruct ss;
|
|
||||||
|
|
||||||
ss.selectRule = esm.getHString();
|
|
||||||
esm.isEmptyOrGetName();
|
|
||||||
|
|
||||||
if(subName.val == REC_INTV)
|
|
||||||
{
|
|
||||||
ss.type = VT_Int;
|
|
||||||
esm.getHT(ss.i);
|
|
||||||
}
|
|
||||||
else if(subName.val == REC_FLTV)
|
|
||||||
{
|
|
||||||
ss.type = VT_Float;
|
|
||||||
esm.getHT(ss.f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
esm.fail("INFO.SCVR must precede INTV or FLTV, not "
|
|
||||||
+ subName.toString());
|
|
||||||
|
|
||||||
selects.push_back(ss);
|
|
||||||
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subName.val == REC_BNAM)
|
|
||||||
{
|
|
||||||
resultScript = esm.getHString();
|
|
||||||
if(esm.isEmptyOrGetName()) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
questStatus = QS_None;
|
|
||||||
|
|
||||||
if (subName.val == REC_QSTN) questStatus = QS_Name;
|
|
||||||
else if(subName.val == REC_QSTF) questStatus = QS_Finished;
|
|
||||||
else if(subName.val == REC_QSTR) questStatus = QS_Restart;
|
|
||||||
else if(subName.val == REC_DELE) questStatus = QS_Deleted;
|
|
||||||
else
|
|
||||||
esm.fail("Don't know what to do with " + subName.toString() + " in INFO " + id);
|
|
||||||
|
|
||||||
if(questStatus != QS_None)
|
|
||||||
// Skip rest of record
|
|
||||||
esm.skipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sound::load(ESMReader &esm)
|
|
||||||
{
|
|
||||||
sound = esm.getHNString("FNAM");
|
|
||||||
esm.getHNT(data, "DATA", 3);
|
|
||||||
/*
|
|
||||||
cout << "vol=" << (int)data.volume
|
|
||||||
<< " min=" << (int)data.minRange
|
|
||||||
<< " max=" << (int)data.maxRange
|
|
||||||
<< endl;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,11 @@
|
|||||||
|
#include "loadacti.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Activator::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
#include "loadalch.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Potion::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "ALDT", 12);
|
||||||
|
effects.load(esm);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#include "loadappa.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Apparatus::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "AADT", 16);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNString("ITEX");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#include "loadarmo.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void PartReferenceList::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
while (esm.isNextSub("INDX"))
|
||||||
|
{
|
||||||
|
PartReference pr;
|
||||||
|
esm.getHT(pr.part); // The INDX byte
|
||||||
|
pr.male = esm.getHNOString("BNAM");
|
||||||
|
pr.female = esm.getHNOString("CNAM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Armor::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
esm.getHNT(data, "AODT", 24);
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
parts.load(esm);
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#include "loadbody.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void BodyPart::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "BYDT", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
#include "loadbook.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Book::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "BKDT", 20);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
text = esm.getHNOString("TEXT");
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#include "loadbsgn.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void BirthSign::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
texture = esm.getHNOString("TNAM");
|
||||||
|
description = esm.getHNOString("DESC");
|
||||||
|
|
||||||
|
powers.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
#include "loadcell.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Cell::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
// Ignore this for now, it might mean we should delete the entire
|
||||||
|
// cell?
|
||||||
|
if (esm.isNextSub("DELE"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
esm.getHNT(data, "DATA", 12);
|
||||||
|
|
||||||
|
// Water level
|
||||||
|
water = 0;
|
||||||
|
|
||||||
|
if (data.flags & Interior)
|
||||||
|
{
|
||||||
|
// Interior cells
|
||||||
|
|
||||||
|
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
||||||
|
esm.getHT(water);
|
||||||
|
|
||||||
|
// Quasi-exterior cells have a region (which determines the
|
||||||
|
// weather), pure interior cells have ambient lighting
|
||||||
|
// instead.
|
||||||
|
if (data.flags & QuasiEx)
|
||||||
|
region = esm.getHNOString("RGNN");
|
||||||
|
else
|
||||||
|
esm.getHNT(ambi, "AMBI", 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Exterior cells
|
||||||
|
region = esm.getHNOString("RGNN");
|
||||||
|
esm.getHNOT(mapColor, "NAM5");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save position of the cell references and move on
|
||||||
|
context = esm.getContext();
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cell::restore(ESMReader &esm) const
|
||||||
|
{
|
||||||
|
esm.restoreContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
|
{
|
||||||
|
if (!esm.hasMoreSubs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Number of references in the cell? Maximum once in each cell,
|
||||||
|
// but not always at the beginning, and not always right. In other
|
||||||
|
// words, completely useless.
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
esm.getHNOT(i, "NAM0");
|
||||||
|
}
|
||||||
|
|
||||||
|
esm.getHNT(ref.refnum, "FRMR");
|
||||||
|
ref.refID = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
// getHNOT will not change the existing value if the subrecord is
|
||||||
|
// missing
|
||||||
|
ref.scale = 1.0;
|
||||||
|
esm.getHNOT(ref.scale, "XSCL");
|
||||||
|
|
||||||
|
ref.owner = esm.getHNOString("ANAM");
|
||||||
|
ref.glob = esm.getHNOString("BNAM");
|
||||||
|
ref.soul = esm.getHNOString("XSOL");
|
||||||
|
|
||||||
|
ref.faction = esm.getHNOString("CNAM");
|
||||||
|
ref.factIndex = -1;
|
||||||
|
esm.getHNOT(ref.factIndex, "INDX");
|
||||||
|
|
||||||
|
ref.charge = -1.0;
|
||||||
|
esm.getHNOT(ref.charge, "XCHG");
|
||||||
|
|
||||||
|
ref.intv = 0;
|
||||||
|
ref.nam9 = 0;
|
||||||
|
esm.getHNOT(ref.intv, "INTV");
|
||||||
|
esm.getHNOT(ref.nam9, "NAM9");
|
||||||
|
|
||||||
|
// Present for doors that teleport you to another cell.
|
||||||
|
if (esm.isNextSub("DODT"))
|
||||||
|
{
|
||||||
|
ref.teleport = true;
|
||||||
|
esm.getHT(ref.doorDest);
|
||||||
|
ref.destCell = esm.getHNOString("DNAM");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ref.teleport = false;
|
||||||
|
|
||||||
|
// Integer, despite the name suggesting otherwise
|
||||||
|
ref.lockLevel = 0;
|
||||||
|
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||||
|
ref.key = esm.getHNOString("KNAM");
|
||||||
|
ref.trap = esm.getHNOString("TNAM");
|
||||||
|
|
||||||
|
ref.unam = 0;
|
||||||
|
ref.fltv = 0;
|
||||||
|
esm.getHNOT(ref.unam, "UNAM");
|
||||||
|
esm.getHNOT(ref.fltv, "FLTV");
|
||||||
|
|
||||||
|
esm.getHNT(ref.pos, "DATA", 24);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
#include "loadclot.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Clothing::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "CTDT", 12);
|
||||||
|
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
|
||||||
|
parts.load(esm);
|
||||||
|
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
#include "loadcont.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void InventoryList::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
ContItem ci;
|
||||||
|
while (esm.isNextSub("NPCO"))
|
||||||
|
{
|
||||||
|
esm.getHT(ci, 36);
|
||||||
|
list.push_back(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(weight, "CNDT", 4);
|
||||||
|
esm.getHNT(flags, "FLAG", 4);
|
||||||
|
|
||||||
|
if (flags & 0xf4)
|
||||||
|
esm.fail("Unknown flags");
|
||||||
|
if (!(flags & 0x8))
|
||||||
|
esm.fail("Flag 8 not set");
|
||||||
|
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
|
||||||
|
inventory.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
#include "loadcrea.hpp"
|
||||||
|
|
||||||
|
namespace ESM {
|
||||||
|
|
||||||
|
void Creature::load(ESMReader &esm, const std::string& id)
|
||||||
|
{
|
||||||
|
mId = id;
|
||||||
|
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
original = esm.getHNOString("CNAM");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
|
||||||
|
esm.getHNT(data, "NPDT", 96);
|
||||||
|
|
||||||
|
esm.getHNT(flags, "FLAG");
|
||||||
|
scale = 1.0;
|
||||||
|
esm.getHNOT(scale, "XSCL");
|
||||||
|
|
||||||
|
inventory.load(esm);
|
||||||
|
|
||||||
|
// More subrecords:
|
||||||
|
|
||||||
|
// AIDT - data (12 bytes, unknown)
|
||||||
|
// AI_W - wander (14 bytes, i don't understand it)
|
||||||
|
// short distance
|
||||||
|
// byte duration
|
||||||
|
// byte timeOfDay
|
||||||
|
// byte idle[10]
|
||||||
|
//
|
||||||
|
// Rest is optional:
|
||||||
|
// AI_T - travel?
|
||||||
|
// AI_F - follow?
|
||||||
|
// AI_E - escort?
|
||||||
|
// AI_A - activate?
|
||||||
|
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#include "loaddial.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Dialogue::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getSubNameIs("DATA");
|
||||||
|
esm.getSubHeader();
|
||||||
|
int si = esm.getSubSize();
|
||||||
|
if (si == 1)
|
||||||
|
esm.getT(type);
|
||||||
|
else if (si == 4)
|
||||||
|
{
|
||||||
|
// These are just markers, their values are not used.
|
||||||
|
int i;
|
||||||
|
esm.getT(i);
|
||||||
|
esm.getHNT(i, "DELE");
|
||||||
|
type = Deleted;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esm.fail("Unknown sub record size");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#include "loaddoor.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Door::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
openSound = esm.getHNOString("SNAM");
|
||||||
|
closeSound = esm.getHNOString("ANAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
#include "loadench.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Enchantment::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(data, "ENDT", 16);
|
||||||
|
effects.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
#include "loadfact.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Faction::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
|
||||||
|
// Read rank names. These are optional.
|
||||||
|
int i = 0;
|
||||||
|
while (esm.isNextSub("RNAM") && i < 10)
|
||||||
|
ranks[i++] = esm.getHString();
|
||||||
|
|
||||||
|
// Main data struct
|
||||||
|
esm.getHNT(data, "FADT", 240);
|
||||||
|
|
||||||
|
if (data.isHidden > 1)
|
||||||
|
esm.fail("Unknown flag!");
|
||||||
|
|
||||||
|
// Read faction response values
|
||||||
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
Reaction r;
|
||||||
|
r.faction = esm.getHNString("ANAM");
|
||||||
|
esm.getHNT(r.reaction, "INTV");
|
||||||
|
reactions.push_back(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#include "loadglob.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Global::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
VarType t;
|
||||||
|
std::string tmp = esm.getHNString("FNAM");
|
||||||
|
if (tmp == "s")
|
||||||
|
t = VT_Short;
|
||||||
|
else if (tmp == "l")
|
||||||
|
t = VT_Int;
|
||||||
|
else if (tmp == "f")
|
||||||
|
t = VT_Float;
|
||||||
|
else
|
||||||
|
esm.fail("Illegal global variable type " + tmp);
|
||||||
|
type = t;
|
||||||
|
|
||||||
|
// Note: Both floats and longs are represented as floats.
|
||||||
|
esm.getHNT(value, "FLTV");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
#include "loadgmst.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
// Some handy macros
|
||||||
|
#define cI(s,x) { if(id == (s)) return (i == (x)); }
|
||||||
|
#define cF(s,x) { if(id == (s)) return (f == (x)); }
|
||||||
|
#define cS(s,x) { if(id == (s)) return (str == (x)); }
|
||||||
|
|
||||||
|
bool GameSetting::isDirtyTribunal()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Here, id contains the game setting name, and we check the
|
||||||
|
setting for certain values. If it matches, this is a "dirty"
|
||||||
|
entry. The correct entry (as defined in Tribunal and Bloodmoon
|
||||||
|
esms) are given in the comments. Many of the values are correct,
|
||||||
|
and are marked as 'same'. We still ignore them though, as they
|
||||||
|
are still in the wrong file and might override custom values
|
||||||
|
from other mods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
cS("sProfitValue", "Profit Value"); // 'Profit:'
|
||||||
|
cS("sEditNote", "Edit Note"); // same
|
||||||
|
cS("sDeleteNote", "Delete Note?"); // same
|
||||||
|
cS("sMaxSale", "Max Sale"); // 'Seller Max'
|
||||||
|
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon'
|
||||||
|
cS("sTeleportDisabled",
|
||||||
|
"Teleportation magic does not work here.");// same
|
||||||
|
cS("sLevitateDisabled",
|
||||||
|
"Levitation magic does not work here."); // same
|
||||||
|
cS("sCompanionShare", "Companion Share"); // 'Share'
|
||||||
|
cS("sCompanionWarningButtonOne",
|
||||||
|
"Let the mercenary quit."); // same
|
||||||
|
cS("sCompanionWarningButtonTwo",
|
||||||
|
"Return to Companion Share display."); // same
|
||||||
|
cS("sCompanionWarningMessage",
|
||||||
|
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value.");
|
||||||
|
// 'Your mercenary is poorer now than when he contracted with
|
||||||
|
// you. Your mercenary will quit if you do not give him gold
|
||||||
|
// or goods to bring his Profit to a positive value.'
|
||||||
|
// [The difference here is "Profit Value" -> "Profit"]
|
||||||
|
|
||||||
|
// Strings that matches the id
|
||||||
|
cS("sEffectSummonFabricant", id);// 'Summon Fabricant'
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bloodmoon variant
|
||||||
|
bool GameSetting::isDirtyBloodmoon()
|
||||||
|
{
|
||||||
|
// Strings
|
||||||
|
cS("sWerewolfPopup", "Werewolf"); // same
|
||||||
|
cS("sWerewolfRestMessage",
|
||||||
|
"You cannot rest in werewolf form."); // same
|
||||||
|
cS("sWerewolfRefusal",
|
||||||
|
"You cannot do this as a werewolf."); // same
|
||||||
|
cS("sWerewolfAlarmMessage",
|
||||||
|
"You have been detected changing from a werewolf state.");
|
||||||
|
// 'You have been detected as a known werewolf.'
|
||||||
|
|
||||||
|
// Strings that matches the id
|
||||||
|
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon'
|
||||||
|
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon'
|
||||||
|
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
|
||||||
|
cS("sMagicCreature04ID", id); // same
|
||||||
|
cS("sMagicCreature05ID", id); // same
|
||||||
|
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
|
||||||
|
cS("sEffectSummonCreature02", id); // 'Calf Bear'
|
||||||
|
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
|
||||||
|
cS("sEffectSummonCreature04", id); // same
|
||||||
|
cS("sEffectSummonCreature05", id); // same
|
||||||
|
|
||||||
|
// Integers
|
||||||
|
cI("iWereWolfBounty", 10000); // 1000
|
||||||
|
cI("iWereWolfFightMod", 100); // same
|
||||||
|
cI("iWereWolfFleeMod", 100); // same
|
||||||
|
cI("iWereWolfLevelToAttack", 20); // same
|
||||||
|
|
||||||
|
// Floats
|
||||||
|
cF("fFleeDistance", 3000); // same
|
||||||
|
cF("fCombatDistanceWerewolfMod", 0.3); // same
|
||||||
|
cF("fWereWolfFatigue", 400); // same
|
||||||
|
cF("fWereWolfEnchant", 1); // 0
|
||||||
|
cF("fWereWolfArmorer", 1); // 0
|
||||||
|
cF("fWereWolfBlock", 1); // 0
|
||||||
|
cF("fWereWolfSneak", 1); // 95
|
||||||
|
cF("fWereWolfDestruction", 1); // 0
|
||||||
|
cF("fWereWolfEndurance", 150); // same
|
||||||
|
cF("fWereWolfConjuration", 1); // 0
|
||||||
|
cF("fWereWolfRestoration", 1); // 0
|
||||||
|
cF("fWereWolfAthletics", 150); // 50
|
||||||
|
cF("fWereWolfLuck", 1); // 25
|
||||||
|
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
|
||||||
|
cF("fWereWolfMediumArmor", 1); // 0
|
||||||
|
cF("fWereWolfShortBlade", 1); // 0
|
||||||
|
cF("fWereWolfAcrobatics", 150); // 80
|
||||||
|
cF("fWereWolfSpeechcraft", 1); // 0
|
||||||
|
cF("fWereWolfAlteration", 1); // 0
|
||||||
|
cF("fWereWolfIllusion", 1); // 0
|
||||||
|
cF("fWereWolfLongBlade", 1); // 0
|
||||||
|
cF("fWereWolfMarksman", 1); // 0
|
||||||
|
cF("fWereWolfHandtoHand", 100); // same
|
||||||
|
cF("fWereWolfIntellegence", 1); // 0
|
||||||
|
cF("fWereWolfAlchemy", 1); // 0
|
||||||
|
cF("fWereWolfUnarmored", 100); // same
|
||||||
|
cF("fWereWolfAxe", 1); // 0
|
||||||
|
cF("fWereWolfRunMult", 1.5); // 1.3
|
||||||
|
cF("fWereWolfMagicka", 100); // same
|
||||||
|
cF("fWereWolfAgility", 150); // same
|
||||||
|
cF("fWereWolfBluntWeapon", 1); // 0
|
||||||
|
cF("fWereWolfSecurity", 1); // 0
|
||||||
|
cF("fWereWolfPersonality", 1); // 0
|
||||||
|
cF("fWereWolfMerchantile", 1); // 0
|
||||||
|
cF("fWereWolfHeavyArmor", 1); // 0
|
||||||
|
cF("fWereWolfSpear", 1); // 0
|
||||||
|
cF("fWereWolfStrength", 150); // same
|
||||||
|
cF("fWereWolfHealth", 2); // same
|
||||||
|
cF("fWereWolfMysticism", 1); // 0
|
||||||
|
cF("fWereWolfLightArmor", 1); // 0
|
||||||
|
cF("fWereWolfWillPower", 1); // 0
|
||||||
|
cF("fWereWolfSpeed", 150); // 90
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSetting::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
assert(id != "");
|
||||||
|
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
// We are apparently allowed to be empty
|
||||||
|
if (!esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
type = VT_None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load some data
|
||||||
|
esm.getSubName();
|
||||||
|
NAME n = esm.retSubName();
|
||||||
|
if (n == "STRV")
|
||||||
|
{
|
||||||
|
str = esm.getHString();
|
||||||
|
type = VT_String;
|
||||||
|
}
|
||||||
|
else if (n == "INTV")
|
||||||
|
{
|
||||||
|
esm.getHT(i);
|
||||||
|
type = VT_Int;
|
||||||
|
}
|
||||||
|
else if (n == "FLTV")
|
||||||
|
{
|
||||||
|
esm.getHT(f);
|
||||||
|
type = VT_Float;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esm.fail("Unwanted subrecord type");
|
||||||
|
|
||||||
|
int spf = esm.getSpecial();
|
||||||
|
|
||||||
|
// Check if this is one of the dirty values mentioned above. If it
|
||||||
|
// is, we set the dirty flag. This will ONLY work if you've set
|
||||||
|
// the 'id' string correctly before calling load().
|
||||||
|
|
||||||
|
if ((spf != SF_Tribunal && isDirtyTribunal()) || (spf != SF_Bloodmoon
|
||||||
|
&& isDirtyBloodmoon()))
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
#include "loadinfo.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void DialInfo::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
id = esm.getHNString("INAM");
|
||||||
|
prev = esm.getHNString("PNAM");
|
||||||
|
next = esm.getHNString("NNAM");
|
||||||
|
|
||||||
|
// Not present if deleted
|
||||||
|
if (esm.isNextSub("DATA"))
|
||||||
|
esm.getHT(data, 12);
|
||||||
|
|
||||||
|
// What follows is somewhat spaghetti-ish, but it's worth if for
|
||||||
|
// an extra speedup. INFO is by far the most common record type.
|
||||||
|
|
||||||
|
// subName is a reference to the original, so it changes whenever
|
||||||
|
// a new sub name is read. esm.isEmptyOrGetName() will get the
|
||||||
|
// next name for us, or return true if there are no more records.
|
||||||
|
esm.getSubName();
|
||||||
|
const NAME &subName = esm.retSubName();
|
||||||
|
|
||||||
|
if (subName.val == REC_ONAM)
|
||||||
|
{
|
||||||
|
actor = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_RNAM)
|
||||||
|
{
|
||||||
|
race = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_CNAM)
|
||||||
|
{
|
||||||
|
clas = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
factionLess = false;
|
||||||
|
if (subName.val == REC_FNAM)
|
||||||
|
{
|
||||||
|
npcFaction = esm.getHString();
|
||||||
|
if (npcFaction == "FFFF")
|
||||||
|
factionLess = true;
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_ANAM)
|
||||||
|
{
|
||||||
|
cell = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_DNAM)
|
||||||
|
{
|
||||||
|
pcFaction = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_SNAM)
|
||||||
|
{
|
||||||
|
sound = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subName.val == REC_NAME)
|
||||||
|
{
|
||||||
|
response = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (subName.val == REC_SCVR)
|
||||||
|
{
|
||||||
|
SelectStruct ss;
|
||||||
|
|
||||||
|
ss.selectRule = esm.getHString();
|
||||||
|
esm.isEmptyOrGetName();
|
||||||
|
|
||||||
|
if (subName.val == REC_INTV)
|
||||||
|
{
|
||||||
|
ss.type = VT_Int;
|
||||||
|
esm.getHT(ss.i);
|
||||||
|
}
|
||||||
|
else if (subName.val == REC_FLTV)
|
||||||
|
{
|
||||||
|
ss.type = VT_Float;
|
||||||
|
esm.getHT(ss.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esm.fail(
|
||||||
|
"INFO.SCVR must precede INTV or FLTV, not "
|
||||||
|
+ subName.toString());
|
||||||
|
|
||||||
|
selects.push_back(ss);
|
||||||
|
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subName.val == REC_BNAM)
|
||||||
|
{
|
||||||
|
resultScript = esm.getHString();
|
||||||
|
if (esm.isEmptyOrGetName())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
questStatus = QS_None;
|
||||||
|
|
||||||
|
if (subName.val == REC_QSTN)
|
||||||
|
questStatus = QS_Name;
|
||||||
|
else if (subName.val == REC_QSTF)
|
||||||
|
questStatus = QS_Finished;
|
||||||
|
else if (subName.val == REC_QSTR)
|
||||||
|
questStatus = QS_Restart;
|
||||||
|
else if (subName.val == REC_DELE)
|
||||||
|
questStatus = QS_Deleted;
|
||||||
|
else
|
||||||
|
esm.fail(
|
||||||
|
"Don't know what to do with " + subName.toString()
|
||||||
|
+ " in INFO " + id);
|
||||||
|
|
||||||
|
if (questStatus != QS_None)
|
||||||
|
// Skip rest of record
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#include "loadingr.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Ingredient::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "IRDT", 56);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
#include "loadland.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Land::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
// Get the grid location
|
||||||
|
esm.getSubNameIs("INTV");
|
||||||
|
esm.getSubHeaderIs(8);
|
||||||
|
esm.getT<int>(X);
|
||||||
|
esm.getT<int>(Y);
|
||||||
|
|
||||||
|
esm.getHNT(flags, "DATA");
|
||||||
|
|
||||||
|
// Store the file position
|
||||||
|
context = esm.getContext();
|
||||||
|
|
||||||
|
hasData = false;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
// Skip these here. Load the actual data when the cell is loaded.
|
||||||
|
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
||||||
|
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
||||||
|
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
||||||
|
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
||||||
|
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
||||||
|
|
||||||
|
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||||
|
// landscape.
|
||||||
|
hasData = (cnt == 3);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
#include "loadlevlist.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void LeveledListBase::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(flags, "DATA");
|
||||||
|
esm.getHNT(chanceNone, "NNAM");
|
||||||
|
|
||||||
|
if (esm.isNextSub("INDX"))
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
esm.getHT(len);
|
||||||
|
list.resize(len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Merge with an existing lists here. This can be done
|
||||||
|
// simply by adding the lists together, making sure that they are
|
||||||
|
// sorted by level. A better way might be to exclude repeated
|
||||||
|
// items. Also, some times we don't want to merge lists, just
|
||||||
|
// overwrite. Figure out a way to give the user this option.
|
||||||
|
|
||||||
|
for (size_t i = 0; i < list.size(); i++)
|
||||||
|
{
|
||||||
|
LevelItem &li = list[i];
|
||||||
|
li.id = esm.getHNString(recName);
|
||||||
|
esm.getHNT(li.level, "INTV");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
#include "loadligh.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Light::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
assert(sizeof(data) == 24);
|
||||||
|
esm.getHNT(data, "LHDT", 24);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
sound = esm.getHNOString("SNAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#include "loadlocks.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Tool::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
|
||||||
|
esm.getSubName();
|
||||||
|
NAME n = esm.retSubName();
|
||||||
|
// The data name varies, RIDT for repair items, LKDT for lock
|
||||||
|
// picks, PBDT for probes
|
||||||
|
|
||||||
|
esm.getHT(data, 16);
|
||||||
|
|
||||||
|
if (n == "RIDT")
|
||||||
|
{
|
||||||
|
// Swap t.data.quality and t.data.uses for repair items (sigh)
|
||||||
|
float tmp = *((float*) &data.uses);
|
||||||
|
data.uses = *((int*) &data.quality);
|
||||||
|
data.quality = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
#include "loadltex.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void LandTexture::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(index, "INTV");
|
||||||
|
texture = esm.getHNString("DATA");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
#include "loadmgef.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void MagicEffect::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(index, "INDX");
|
||||||
|
|
||||||
|
esm.getHNT(data, "MEDT", 36);
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
particle = esm.getHNOString("PTEX");
|
||||||
|
|
||||||
|
boltSound = esm.getHNOString("BSND");
|
||||||
|
castSound = esm.getHNOString("CSND");
|
||||||
|
hitSound = esm.getHNOString("HSND");
|
||||||
|
areaSound = esm.getHNOString("ASND");
|
||||||
|
|
||||||
|
casting = esm.getHNOString("CVFX");
|
||||||
|
bolt = esm.getHNOString("BVFX");
|
||||||
|
hit = esm.getHNOString("HVFX");
|
||||||
|
area = esm.getHNOString("AVFX");
|
||||||
|
|
||||||
|
description = esm.getHNOString("DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue