Merge remote-tracking branch 'scrawl/master'

This commit is contained in:
Marc Zinnschlag 2015-02-17 11:55:12 +01:00
commit 8686ffcedb
50 changed files with 958 additions and 546 deletions

View file

@ -43,13 +43,17 @@ namespace ESSImport
float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes
unsigned char mUnknown4[4]; unsigned char mUnknown4[4];
unsigned int mGoldPool; unsigned int mGoldPool;
unsigned char mUnknown5[4]; unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe
// this one is for respawning?
unsigned char mUnknown5[3];
}; };
struct ACSC struct ACSC
{ {
unsigned char mUnknown1[17]; unsigned char mUnknown1[17];
unsigned char mFlags; // ACSCFlags unsigned char mFlags; // ACSCFlags
unsigned char mUnknown2[94]; unsigned char mUnknown2[22];
unsigned char mCorpseClearCountdown; // hours?
unsigned char mUnknown3[71];
}; };
#pragma pack(pop) #pragma pack(pop)

View file

@ -14,10 +14,10 @@ namespace ESSImport
float scale; float scale;
esm.getHNOT(scale, "XSCL"); esm.getHNOT(scale, "XSCL");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

View file

@ -2,6 +2,7 @@
#define OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H
#include "importinventory.hpp" #include "importinventory.hpp"
#include <components/esm/aipackage.hpp>
namespace ESM namespace ESM
{ {
@ -17,6 +18,7 @@ namespace ESSImport
int mIndex; int mIndex;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

View file

@ -9,10 +9,9 @@ namespace ESSImport
{ {
esm.getHNT(mNPDT, "NPDT"); esm.getHNT(mNPDT, "NPDT");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

View file

@ -27,6 +27,7 @@ namespace ESSImport
} mNPDT; } mNPDT;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader &esm); void load(ESM::ESMReader &esm);
}; };

View file

@ -199,7 +199,7 @@ struct JournalViewModelImpl : JournalViewModel
}; };
void visitQuestNames (bool active_only, boost::function <void (const std::string&)> visitor) const void visitQuestNames (bool active_only, boost::function <void (const std::string&, bool)> visitor) const
{ {
MWBase::Journal * journal = MWBase::Environment::get ().getJournal (); MWBase::Journal * journal = MWBase::Environment::get ().getJournal ();
@ -231,7 +231,7 @@ struct JournalViewModelImpl : JournalViewModel
if (visitedQuests.find(quest.getName()) != visitedQuests.end()) if (visitedQuests.find(quest.getName()) != visitedQuests.end())
continue; continue;
visitor (quest.getName()); visitor (quest.getName(), isFinished);
visitedQuests.insert(quest.getName()); visitedQuests.insert(quest.getName());
} }

View file

@ -67,8 +67,8 @@ namespace MWGui
/// returns true if their are no journal entries to display /// returns true if their are no journal entries to display
virtual bool isEmpty () const = 0; virtual bool isEmpty () const = 0;
/// walks the active and optionally completed, quests providing the name /// walks the active and optionally completed, quests providing the name and completed status
virtual void visitQuestNames (bool active_only, boost::function <void (const std::string&)> visitor) const = 0; virtual void visitQuestNames (bool active_only, boost::function <void (const std::string&, bool)> visitor) const = 0;
/// walks over the journal entries related to all quests with the given name /// walks over the journal entries related to all quests with the given name
/// If \a questName is empty, simply visits all journal entries /// If \a questName is empty, simply visits all journal entries

View file

@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <MyGUI_Button.h>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/function.hpp> #include <boost/function.hpp>
@ -428,11 +429,24 @@ namespace
AddNamesToList(Gui::MWList* list) : mList(list) {} AddNamesToList(Gui::MWList* list) : mList(list) {}
Gui::MWList* mList; Gui::MWList* mList;
void operator () (const std::string& name) void operator () (const std::string& name, bool finished=false)
{ {
mList->addItem(name); mList->addItem(name);
} }
}; };
struct SetNamesInactive
{
SetNamesInactive(Gui::MWList* list) : mList(list) {}
Gui::MWList* mList;
void operator () (const std::string& name, bool finished)
{
if (finished)
{
mList->getItemWidget(name)->setStateSelected(true);
}
}
};
void notifyQuests(MyGUI::Widget* _sender) void notifyQuests(MyGUI::Widget* _sender)
{ {
@ -453,6 +467,12 @@ namespace
mModel->visitQuestNames(!mAllQuests, add); mModel->visitQuestNames(!mAllQuests, add);
list->adjustSize(); list->adjustSize();
if (mAllQuests)
{
SetNamesInactive setInactive(list);
mModel->visitQuestNames(!mAllQuests, setInactive);
}
} }
void notifyShowAll(MyGUI::Widget* _sender) void notifyShowAll(MyGUI::Widget* _sender)

View file

@ -207,16 +207,6 @@ namespace MWMechanics
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
// can't fight if attacker can't go where target is. E.g. A fish can't attack person on land.
if (!actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
{
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
return true;
}
//Update every frame //Update every frame
bool& combatMove = storage.mCombatMove; bool& combatMove = storage.mCombatMove;
@ -476,6 +466,19 @@ namespace MWMechanics
// for distant combat we should know if target is in LOS even if distToTarget < rangeAttack // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack
bool inLOS = distantCombat ? world->getLOS(actor, target) : true; bool inLOS = distantCombat ? world->getLOS(actor, target) : true;
// can't fight if attacker can't go where target is. E.g. A fish can't attack person on land.
if (distToTarget >= rangeAttack
&& !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
{
// TODO: start fleeing?
movement.mPosition[0] = 0;
movement.mPosition[1] = 0;
movement.mPosition[2] = 0;
readyToAttack = false;
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
return false;
}
// (within attack dist) || (not quite attack dist while following) // (within attack dist) || (not quite attack dist while following)
if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck))) if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck)))
{ {

View file

@ -113,7 +113,7 @@ void RippleSimulation::update(float dt, Ogre::Vector2 position)
position.z = 0; // Z is set by the Scene Node position.z = 0; // Z is set by the Scene Node
rotSpeed = mRippleRotSpeed; rotSpeed = mRippleRotSpeed;
rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI)); rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI));
created->setDimensions(50,50); created->setDimensions(mParticleSystem->getDefaultWidth(), mParticleSystem->getDefaultHeight());
} }
} }

View file

@ -41,29 +41,6 @@ namespace ESM
} }
void AIPackageList::load(ESMReader &esm)
{
mList.clear();
while (esm.hasMoreSubs()) {
// initialize every iteration
esm.getSubName();
switch (esm.retSubName().val)
{
case AI_Wander:
case AI_Activate:
case AI_Escort:
case AI_Follow:
case AI_Travel:
case AI_CNDT:
add(esm);
break;
default:
return;
}
}
}
void AIPackageList::save(ESMWriter &esm) const void AIPackageList::save(ESMWriter &esm) const
{ {
typedef std::vector<AIPackage>::const_iterator PackageIter; typedef std::vector<AIPackage>::const_iterator PackageIter;

View file

@ -94,9 +94,6 @@ namespace ESM
/// Add a single AIPackage, assumes subrecord name was already read /// Add a single AIPackage, assumes subrecord name was already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// TODO: remove this method. The ESM format does not guarantee that all AI packages follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };
} }

View file

@ -8,13 +8,18 @@ namespace ESM {
void EffectList::load(ESMReader &esm) void EffectList::load(ESMReader &esm)
{ {
mList.clear(); mList.clear();
ENAMstruct s;
while (esm.isNextSub("ENAM")) { while (esm.isNextSub("ENAM")) {
esm.getHT(s, 24); add(esm);
mList.push_back(s);
} }
} }
void EffectList::add(ESMReader &esm)
{
ENAMstruct s;
esm.getHT(s, 24);
mList.push_back(s);
}
void EffectList::save(ESMWriter &esm) const void EffectList::save(ESMWriter &esm) const
{ {
for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) { for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {

View file

@ -29,11 +29,15 @@ namespace ESM
}; };
#pragma pack(pop) #pragma pack(pop)
/// EffectList, ENAM subrecord
struct EffectList struct EffectList
{ {
std::vector<ENAMstruct> mList; std::vector<ENAMstruct> mList;
/// Load one effect, assumes subrecord name was already read
void add(ESMReader &esm);
/// Load all effects
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,18 +8,34 @@ namespace ESM
{ {
unsigned int Activator::sRecordId = REC_ACTI; unsigned int Activator::sRecordId = REC_ACTI;
void Activator::load(ESMReader &esm) void Activator::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); while (esm.hasMoreSubs())
mName = esm.getHNOString("FNAM"); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
} uint32_t name = esm.retSubName().val;
void Activator::save(ESMWriter &esm) const switch (name)
{ {
esm.writeHNCString("MODL", mModel); case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNOCString("FNAM", mName); mModel = esm.getHString();
esm.writeHNOCString("SCRI", mScript); break;
} case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
}
void Activator::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
}
void Activator::blank() void Activator::blank()
{ {

View file

@ -8,24 +8,51 @@ namespace ESM
{ {
unsigned int Potion::sRecordId = REC_ALCH; unsigned int Potion::sRecordId = REC_ALCH;
void Potion::load(ESMReader &esm) void Potion::load(ESMReader &esm)
{ {
mModel = esm.getHNOString("MODL"); mEffects.mList.clear();
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason bool hasData = false;
mScript = esm.getHNOString("SCRI"); while (esm.hasMoreSubs())
mName = esm.getHNOString("FNAM"); {
esm.getHNT(mData, "ALDT", 12); esm.getSubName();
mEffects.load(esm); uint32_t name = esm.retSubName().val;
} switch (name)
void Potion::save(ESMWriter &esm) const {
{ case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNCString("MODL", mModel); mModel = esm.getHString();
esm.writeHNOCString("TEXT", mIcon); break;
esm.writeHNOCString("SCRI", mScript); case ESM::FourCC<'T','E','X','T'>::value: // not ITEX here for some reason
esm.writeHNOCString("FNAM", mName); mIcon = esm.getHString();
esm.writeHNT("ALDT", mData, 12); break;
mEffects.save(esm); case ESM::FourCC<'S','C','R','I'>::value:
} mScript = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'A','L','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
mEffects.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing ALDT");
}
void Potion::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("TEXT", mIcon);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("ALDT", mData, 12);
mEffects.save(esm);
}
void Potion::blank() void Potion::blank()
{ {

View file

@ -10,25 +10,35 @@ namespace ESM
void Apparatus::load(ESMReader &esm) void Apparatus::load(ESMReader &esm)
{ {
// we will not treat duplicated subrecords as errors here bool hasData = false;
while (esm.hasMoreSubs()) while (esm.hasMoreSubs())
{ {
esm.getSubName(); esm.getSubName();
NAME subName = esm.retSubName(); uint32_t name = esm.retSubName().val;
switch (name)
if (subName == "MODL") {
mModel = esm.getHString(); case ESM::FourCC<'M','O','D','L'>::value:
else if (subName == "FNAM") mModel = esm.getHString();
mName = esm.getHString(); break;
else if (subName == "AADT") case ESM::FourCC<'F','N','A','M'>::value:
esm.getHT(mData); mName = esm.getHString();
else if (subName == "SCRI") break;
mScript = esm.getHString(); case ESM::FourCC<'A','A','D','T'>::value:
else if (subName == "ITEX") esm.getHT(mData);
mIcon = esm.getHString(); hasData = true;
else break;
esm.fail("wrong subrecord type " + subName.toString() + " for APPA record"); case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
if (!hasData)
esm.fail("Missing AADT");
} }
void Apparatus::save(ESMWriter &esm) const void Apparatus::save(ESMWriter &esm) const

View file

@ -11,9 +11,30 @@ namespace ESM
void BodyPart::load(ESMReader &esm) void BodyPart::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = false;
mRace = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
esm.getHNT(mData, "BYDT", 4); {
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mRace = esm.getHString();
break;
case ESM::FourCC<'B','Y','D','T'>::value:
esm.getHT(mData, 4);
hasData = true;
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing BYDT subrecord");
} }
void BodyPart::save(ESMWriter &esm) const void BodyPart::save(ESMWriter &esm) const
{ {

View file

@ -10,11 +10,29 @@ namespace ESM
void BirthSign::load(ESMReader &esm) void BirthSign::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); mPowers.mList.clear();
mTexture = esm.getHNOString("TNAM"); while (esm.hasMoreSubs())
mDescription = esm.getHNOString("DESC"); {
esm.getSubName();
mPowers.load(esm); uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'T','N','A','M'>::value:
mTexture = esm.getHString();
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
case ESM::FourCC<'N','P','C','S'>::value:
mPowers.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
} }
void BirthSign::save(ESMWriter &esm) const void BirthSign::save(ESMWriter &esm) const

View file

@ -10,17 +10,17 @@ namespace ESM
{ {
unsigned int Class::sRecordId = REC_CLAS; unsigned int Class::sRecordId = REC_CLAS;
const Class::Specialization Class::sSpecializationIds[3] = { const Class::Specialization Class::sSpecializationIds[3] = {
Class::Combat, Class::Combat,
Class::Magic, Class::Magic,
Class::Stealth Class::Stealth
}; };
const char *Class::sGmstSpecializationIds[3] = { const char *Class::sGmstSpecializationIds[3] = {
"sSpecializationCombat", "sSpecializationCombat",
"sSpecializationMagic", "sSpecializationMagic",
"sSpecializationStealth" "sSpecializationStealth"
}; };
int& Class::CLDTstruct::getSkill (int index, bool major) int& Class::CLDTstruct::getSkill (int index, bool major)
@ -39,22 +39,40 @@ const char *Class::sGmstSpecializationIds[3] = {
return mSkills[index][major ? 1 : 0]; return mSkills[index][major ? 1 : 0];
} }
void Class::load(ESMReader &esm) void Class::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "CLDT", 60); while (esm.hasMoreSubs())
{
if (mData.mIsPlayable > 1) esm.getSubName();
esm.fail("Unknown bool value"); uint32_t name = esm.retSubName().val;
switch (name)
mDescription = esm.getHNOString("DESC"); {
} case ESM::FourCC<'F','N','A','M'>::value:
void Class::save(ESMWriter &esm) const mName = esm.getHString();
{ break;
esm.writeHNOCString("FNAM", mName); case ESM::FourCC<'C','L','D','T'>::value:
esm.writeHNT("CLDT", mData, 60); esm.getHT(mData, 60);
esm.writeHNOString("DESC", mDescription); if (mData.mIsPlayable > 1)
} esm.fail("Unknown bool value");
hasData = true;
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing CLDT subrecord");
}
void Class::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CLDT", mData, 60);
esm.writeHNOString("DESC", mDescription);
}
void Class::blank() void Class::blank()
{ {

View file

@ -7,60 +7,79 @@
namespace ESM namespace ESM
{ {
void InventoryList::add(ESMReader &esm) void InventoryList::add(ESMReader &esm)
{
ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
}
void InventoryList::load(ESMReader &esm)
{
mList.clear();
while (esm.isNextSub("NPCO"))
{ {
add(esm); ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
} }
}
void InventoryList::save(ESMWriter &esm) const void InventoryList::save(ESMWriter &esm) const
{
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{ {
esm.writeHNT("NPCO", *it, 36); for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{
esm.writeHNT("NPCO", *it, 36);
}
} }
}
unsigned int Container::sRecordId = REC_CONT; unsigned int Container::sRecordId = REC_CONT;
void Container::load(ESMReader &esm) void Container::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mInventory.mList.clear();
mName = esm.getHNOString("FNAM"); bool hasWeight = false;
esm.getHNT(mWeight, "CNDT", 4); bool hasFlags = false;
esm.getHNT(mFlags, "FLAG", 4); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'C','N','D','T'>::value:
esm.getHT(mWeight, 4);
hasWeight = true;
break;
case ESM::FourCC<'F','L','A','G'>::value:
esm.getHT(mFlags, 4);
if (mFlags & 0xf4)
esm.fail("Unknown flags");
if (!(mFlags & 0x8))
esm.fail("Flag 8 not set");
hasFlags = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'N','P','C','O'>::value:
mInventory.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasWeight)
esm.fail("Missing CNDT subrecord");
if (!hasFlags)
esm.fail("Missing FLAG subrecord");
}
if (mFlags & 0xf4) void Container::save(ESMWriter &esm) const
esm.fail("Unknown flags"); {
if (!(mFlags & 0x8)) esm.writeHNCString("MODL", mModel);
esm.fail("Flag 8 not set"); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CNDT", mWeight, 4);
esm.writeHNT("FLAG", mFlags, 4);
mScript = esm.getHNOString("SCRI"); esm.writeHNOCString("SCRI", mScript);
mInventory.load(esm); mInventory.save(esm);
} }
void Container::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CNDT", mWeight, 4);
esm.writeHNT("FLAG", mFlags, 4);
esm.writeHNOCString("SCRI", mScript);
mInventory.save(esm);
}
void Container::blank() void Container::blank()
{ {

View file

@ -22,6 +22,7 @@ struct ContItem
NAME32 mItem; NAME32 mItem;
}; };
/// InventoryList, NPCO subrecord
struct InventoryList struct InventoryList
{ {
std::vector<ContItem> mList; std::vector<ContItem> mList;
@ -29,8 +30,6 @@ struct InventoryList
/// Load one item, assumes subrecord name is already read /// Load one item, assumes subrecord name is already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// TODO: remove this method, the ESM format doesn't guarantee that all ContItems follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,55 +8,94 @@ namespace ESM {
unsigned int Creature::sRecordId = REC_CREA; unsigned int Creature::sRecordId = REC_CREA;
void Creature::load(ESMReader &esm) void Creature::load(ESMReader &esm)
{
mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
mOriginal = esm.getHNOString("CNAM");
mName = esm.getHNOString("FNAM");
mScript = esm.getHNOString("SCRI");
esm.getHNT(mData, "NPDT", 96);
esm.getHNT(mFlags, "FLAG");
mScale = 1.0;
esm.getHNOT(mScale, "XSCL");
mInventory.load(esm);
mSpells.load(esm);
if (esm.isNextSub("AIDT"))
{ {
esm.getHExact(&mAiData, sizeof(mAiData)); mPersistent = esm.getRecordFlags() & 0x0400;
mHasAI = true;
} mAiPackage.mList.clear();
else mInventory.mList.clear();
mSpells.mList.clear();
mScale = 1.f;
mHasAI = false; mHasAI = false;
bool hasNpdt = false;
mAiPackage.load(esm); bool hasFlags = false;
esm.skipRecord(); while (esm.hasMoreSubs())
} {
esm.getSubName();
void Creature::save(ESMWriter &esm) const uint32_t name = esm.retSubName().val;
{ switch (name)
esm.writeHNCString("MODL", mModel); {
esm.writeHNOCString("CNAM", mOriginal); case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNOCString("FNAM", mName); mModel = esm.getHString();
esm.writeHNOCString("SCRI", mScript); break;
esm.writeHNT("NPDT", mData, 96); case ESM::FourCC<'C','N','A','M'>::value:
esm.writeHNT("FLAG", mFlags); mOriginal = esm.getHString();
if (mScale != 1.0) { break;
esm.writeHNT("XSCL", mScale); case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'N','P','D','T'>::value:
esm.getHT(mData, 96);
hasNpdt = true;
break;
case ESM::FourCC<'F','L','A','G'>::value:
esm.getHT(mFlags);
hasFlags = true;
break;
case ESM::FourCC<'X','S','C','L'>::value:
esm.getHT(mScale);
break;
case ESM::FourCC<'N','P','C','O'>::value:
mInventory.add(esm);
break;
case ESM::FourCC<'N','P','C','S'>::value:
mSpells.add(esm);
break;
case ESM::FourCC<'A','I','D','T'>::value:
esm.getHExact(&mAiData, sizeof(mAiData));
mHasAI = true;
break;
case AI_Wander:
case AI_Activate:
case AI_Escort:
case AI_Follow:
case AI_Travel:
case AI_CNDT:
mAiPackage.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasNpdt)
esm.fail("Missing NPDT subrecord");
if (!hasFlags)
esm.fail("Missing FLAG subrecord");
} }
mInventory.save(esm); void Creature::save(ESMWriter &esm) const
mSpells.save(esm); {
if (mHasAI) { esm.writeHNCString("MODL", mModel);
esm.writeHNT("AIDT", mAiData, sizeof(mAiData)); esm.writeHNOCString("CNAM", mOriginal);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNT("NPDT", mData, 96);
esm.writeHNT("FLAG", mFlags);
if (mScale != 1.0) {
esm.writeHNT("XSCL", mScale);
}
mInventory.save(esm);
mSpells.save(esm);
if (mHasAI) {
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
}
mAiPackage.save(esm);
} }
mAiPackage.save(esm);
}
void Creature::blank() void Creature::blank()
{ {

View file

@ -10,8 +10,28 @@ namespace ESM
void Enchantment::load(ESMReader &esm) void Enchantment::load(ESMReader &esm)
{ {
esm.getHNT(mData, "ENDT", 16); mEffects.mList.clear();
mEffects.load(esm); bool hasData = false;
while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'E','N','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
mEffects.add(esm);
break;
default:
esm.fail("Unknown subrecord");
break;
}
}
if (!hasData)
esm.fail("Missing ENDT subrecord");
} }
void Enchantment::save(ESMWriter &esm) const void Enchantment::save(ESMWriter &esm) const

View file

@ -28,27 +28,46 @@ namespace ESM
void Faction::load(ESMReader &esm) void Faction::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); mReactions.clear();
for (int i=0;i<10;++i)
mRanks[i].clear();
// Read rank names. These are optional. int rankCounter=0;
int i = 0; bool hasData = false;
while (esm.isNextSub("RNAM") && i < 10)
mRanks[i++] = esm.getHString();
// Main data struct
esm.getHNT(mData, "FADT", 240);
if (mData.mIsHidden > 1)
esm.fail("Unknown flag!");
// Read faction response values
while (esm.hasMoreSubs()) while (esm.hasMoreSubs())
{ {
std::string faction = esm.getHNString("ANAM"); esm.getSubName();
int reaction; uint32_t name = esm.retSubName().val;
esm.getHNT(reaction, "INTV"); switch (name)
mReactions[faction] = reaction; {
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'R','N','A','M'>::value:
if (rankCounter >= 10)
esm.fail("Rank out of range");
mRanks[rankCounter++] = esm.getHString();
break;
case ESM::FourCC<'F','A','D','T'>::value:
esm.getHT(mData, 240);
if (mData.mIsHidden > 1)
esm.fail("Unknown flag!");
hasData = true;
break;
case ESM::FourCC<'A','N','A','M'>::value:
{
std::string faction = esm.getHString();
int reaction;
esm.getHNT(reaction, "INTV");
mReactions[faction] = reaction;
break;
}
default:
esm.fail("Unknown subrecord");
}
} }
if (!hasData)
esm.fail("Missing FADT subrecord");
} }
void Faction::save(ESMWriter &esm) const void Faction::save(ESMWriter &esm) const
{ {

View file

@ -8,45 +8,71 @@ namespace ESM
{ {
unsigned int Ingredient::sRecordId = REC_INGR; unsigned int Ingredient::sRecordId = REC_INGR;
void Ingredient::load(ESMReader &esm) void Ingredient::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
esm.getHNT(mData, "IRDT", 56);
mScript = esm.getHNOString("SCRI");
mIcon = esm.getHNOString("ITEX");
// horrible hack to fix broken data in records
for (int i=0; i<4; ++i)
{ {
if (mData.mEffectID[i] != 85 && bool hasData = false;
mData.mEffectID[i] != 22 && while (esm.hasMoreSubs())
mData.mEffectID[i] != 17 &&
mData.mEffectID[i] != 79 &&
mData.mEffectID[i] != 74)
{ {
mData.mAttributes[i] = -1; esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'I','R','D','T'>::value:
esm.getHT(mData, 56);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
// is this relevant in cycle from 0 to 4? if (!hasData)
if (mData.mEffectID[i] != 89 && esm.fail("Missing IRDT subrecord");
mData.mEffectID[i] != 26 &&
mData.mEffectID[i] != 21 && // horrible hack to fix broken data in records
mData.mEffectID[i] != 83 && for (int i=0; i<4; ++i)
mData.mEffectID[i] != 78)
{ {
mData.mSkills[i] = -1; if (mData.mEffectID[i] != 85 &&
mData.mEffectID[i] != 22 &&
mData.mEffectID[i] != 17 &&
mData.mEffectID[i] != 79 &&
mData.mEffectID[i] != 74)
{
mData.mAttributes[i] = -1;
}
// is this relevant in cycle from 0 to 4?
if (mData.mEffectID[i] != 89 &&
mData.mEffectID[i] != 26 &&
mData.mEffectID[i] != 21 &&
mData.mEffectID[i] != 83 &&
mData.mEffectID[i] != 78)
{
mData.mSkills[i] = -1;
}
} }
} }
}
void Ingredient::save(ESMWriter &esm) const void Ingredient::save(ESMWriter &esm) const
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("IRDT", mData, 56); esm.writeHNT("IRDT", mData, 56);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
} }
void Ingredient::blank() void Ingredient::blank()
{ {

View file

@ -7,49 +7,51 @@
namespace ESM namespace ESM
{ {
void LevelledListBase::load(ESMReader &esm) void LevelledListBase::load(ESMReader &esm)
{
esm.getHNT(mFlags, "DATA");
esm.getHNT(mChanceNone, "NNAM");
if (esm.isNextSub("INDX"))
{ {
int len; esm.getHNT(mFlags, "DATA");
esm.getHT(len); esm.getHNT(mChanceNone, "NNAM");
mList.resize(len);
}
else
{
esm.skipRecord();
return;
}
// If this levelled list was already loaded by a previous content file, if (esm.isNextSub("INDX"))
// we overwrite the list. Merging lists should probably be left to external tools, {
// with the limited amount of information there is in the records, all merging methods int len;
// will be flawed in some way. For a proper fix the ESM format would have to be changed esm.getHT(len);
// to actually track list changes instead of including the whole list for every file mList.resize(len);
// that does something with that list. }
else
{
// Original engine ignores rest of the record, even if there are items following
mList.clear();
esm.skipRecord();
return;
}
for (size_t i = 0; i < mList.size(); i++) // If this levelled list was already loaded by a previous content file,
{ // we overwrite the list. Merging lists should probably be left to external tools,
LevelItem &li = mList[i]; // with the limited amount of information there is in the records, all merging methods
li.mId = esm.getHNString(mRecName); // will be flawed in some way. For a proper fix the ESM format would have to be changed
esm.getHNT(li.mLevel, "INTV"); // to actually track list changes instead of including the whole list for every file
} // that does something with that list.
}
void LevelledListBase::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mFlags);
esm.writeHNT("NNAM", mChanceNone);
esm.writeHNT<int>("INDX", mList.size());
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it) for (size_t i = 0; i < mList.size(); i++)
{ {
esm.writeHNCString(mRecName, it->mId); LevelItem &li = mList[i];
esm.writeHNT("INTV", it->mLevel); li.mId = esm.getHNString(mRecName);
esm.getHNT(li.mLevel, "INTV");
}
}
void LevelledListBase::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mFlags);
esm.writeHNT("NNAM", mChanceNone);
esm.writeHNT<int>("INDX", mList.size());
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{
esm.writeHNCString(mRecName, it->mId);
esm.writeHNT("INTV", it->mLevel);
}
} }
}
void LevelledListBase::blank() void LevelledListBase::blank()
{ {

View file

@ -8,25 +8,50 @@ namespace ESM
{ {
unsigned int Light::sRecordId = REC_LIGH; unsigned int Light::sRecordId = REC_LIGH;
void Light::load(ESMReader &esm) void Light::load(ESMReader &esm)
{ {
mModel = esm.getHNOString("MODL"); bool hasData = false;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
mIcon = esm.getHNOString("ITEX"); {
assert(sizeof(mData) == 24); esm.getSubName();
esm.getHNT(mData, "LHDT", 24); uint32_t name = esm.retSubName().val;
mScript = esm.getHNOString("SCRI"); switch (name)
mSound = esm.getHNOString("SNAM"); {
} case ESM::FourCC<'M','O','D','L'>::value:
void Light::save(ESMWriter &esm) const mModel = esm.getHString();
{ break;
esm.writeHNCString("MODL", mModel); case ESM::FourCC<'F','N','A','M'>::value:
esm.writeHNOCString("FNAM", mName); mName = esm.getHString();
esm.writeHNOCString("ITEX", mIcon); break;
esm.writeHNT("LHDT", mData, 24); case ESM::FourCC<'I','T','E','X'>::value:
esm.writeHNOCString("SCRI", mScript); mIcon = esm.getHString();
esm.writeHNOCString("SNAM", mSound); break;
} case ESM::FourCC<'L','H','D','T'>::value:
esm.getHT(mData, 24);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'S','N','A','M'>::value:
mSound = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing LHDT subrecord");
}
void Light::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("ITEX", mIcon);
esm.writeHNT("LHDT", mData, 24);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("SNAM", mSound);
}
void Light::blank() void Light::blank()
{ {

View file

@ -8,26 +8,48 @@ namespace ESM
{ {
unsigned int Lockpick::sRecordId = REC_LOCK; unsigned int Lockpick::sRecordId = REC_LOCK;
void Lockpick::load(ESMReader &esm) void Lockpick::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'L','K','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing LKDT subrecord");
}
esm.getHNT(mData, "LKDT", 16); void Lockpick::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
mScript = esm.getHNOString("SCRI"); esm.writeHNT("LKDT", mData, 16);
mIcon = esm.getHNOString("ITEX"); esm.writeHNOString("SCRI", mScript);
} esm.writeHNOCString("ITEX", mIcon);
}
void Lockpick::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("LKDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Lockpick::blank() void Lockpick::blank()
{ {

View file

@ -246,7 +246,7 @@ void MagicEffect::load(ESMReader &esm)
mDescription = esm.getHString(); mDescription = esm.getHString();
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
} }

View file

@ -57,9 +57,9 @@ struct MagicEffect
// Glow color for enchanted items with this effect // Glow color for enchanted items with this effect
int mRed, mGreen, mBlue; int mRed, mGreen, mBlue;
float mUnknown1; float mUnknown1; // Called "Size X" in CS
float mSpeed; // Speed of fired projectile float mSpeed; // Speed of fired projectile
float mUnknown2; float mUnknown2; // Called "Size Cap" in CS
}; // 36 bytes }; // 36 bytes
static const std::map<short,std::string> sNames; static const std::map<short,std::string> sNames;

View file

@ -99,7 +99,7 @@ namespace ESM
mAiPackage.add(esm); mAiPackage.add(esm);
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
if (!hasNpdt) if (!hasNpdt)

View file

@ -8,26 +8,48 @@ namespace ESM
{ {
unsigned int Probe::sRecordId = REC_PROB; unsigned int Probe::sRecordId = REC_PROB;
void Probe::load(ESMReader &esm) void Probe::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'P','B','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing PBDT subrecord");
}
esm.getHNT(mData, "PBDT", 16); void Probe::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
mScript = esm.getHNOString("SCRI"); esm.writeHNT("PBDT", mData, 16);
mIcon = esm.getHNOString("ITEX"); esm.writeHNOString("SCRI", mScript);
} esm.writeHNOCString("ITEX", mIcon);
}
void Probe::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("PBDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Probe::blank() void Probe::blank()
{ {

View file

@ -43,7 +43,7 @@ void Race::load(ESMReader &esm)
mPowers.add(esm); mPowers.add(esm);
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
if (!hasData) if (!hasData)

View file

@ -10,13 +10,35 @@ namespace ESM
void Repair::load(ESMReader &esm) void Repair::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getHNT(mData, "RIDT", 16); esm.getSubName();
uint32_t name = esm.retSubName().val;
mScript = esm.getHNOString("SCRI"); switch (name)
mIcon = esm.getHNOString("ITEX"); {
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'R','I','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing RIDT subrecord");
} }
void Repair::save(ESMWriter &esm) const void Repair::save(ESMWriter &esm) const

View file

@ -9,17 +9,7 @@ namespace ESM
unsigned int Script::sRecordId = REC_SCPT; unsigned int Script::sRecordId = REC_SCPT;
void Script::load(ESMReader &esm) void Script::loadSCVR(ESMReader &esm)
{
SCHD data;
esm.getHNT(data, "SCHD", 52);
mData = data.mData;
mId = data.mName.toString();
mVarNames.clear();
// List of local variables
if (esm.isNextSub("SCVR"))
{ {
int s = mData.mStringTableSize; int s = mData.mStringTableSize;
@ -66,58 +56,70 @@ void Script::load(ESMReader &esm)
} }
} }
// Script mData void Script::load(ESMReader &esm)
if (esm.isNextSub("SCDT"))
{ {
mScriptData.resize(mData.mScriptDataSize); SCHD data;
esm.getHExact(&mScriptData[0], mScriptData.size()); esm.getHNT(data, "SCHD", 52);
} mData = data.mData;
mId = data.mName.toString();
// Script text mVarNames.clear();
mScriptText = esm.getHNOString("SCTX");
// NOTE: A minor hack/workaround... while (esm.hasMoreSubs())
//
// MAO_Containers.esp from Morrowind Acoustic Overhaul has SCVR records
// at the end (see Bug #1849). Since OpenMW does not use SCVR subrecords
// for variable names just skip these as a quick fix. An alternative
// solution would be to decode and validate SCVR subrecords even if they
// appear here.
if (esm.isNextSub("SCVR")) {
esm.skipHSub();
}
}
void Script::save(ESMWriter &esm) const
{
std::string varNameString;
if (!mVarNames.empty())
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
varNameString.append(*it);
SCHD data;
memset(&data, 0, sizeof(data));
data.mData = mData;
memcpy(data.mName.name, mId.c_str(), mId.size());
esm.writeHNT("SCHD", data, 52);
if (!mVarNames.empty())
{
esm.startSubRecord("SCVR");
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
{ {
esm.writeHCString(*it); esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'S','C','V','R'>::value:
// list of local variables
loadSCVR(esm);
break;
case ESM::FourCC<'S','C','D','T'>::value:
// compiled script
mScriptData.resize(mData.mScriptDataSize);
esm.getHExact(&mScriptData[0], mScriptData.size());
break;
case ESM::FourCC<'S','C','T','X'>::value:
mScriptText = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
esm.endRecord("SCVR");
} }
esm.startSubRecord("SCDT"); void Script::save(ESMWriter &esm) const
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize); {
esm.endRecord("SCDT"); std::string varNameString;
if (!mVarNames.empty())
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
varNameString.append(*it);
esm.writeHNOString("SCTX", mScriptText); SCHD data;
} memset(&data, 0, sizeof(data));
data.mData = mData;
memcpy(data.mName.name, mId.c_str(), mId.size());
esm.writeHNT("SCHD", data, 52);
if (!mVarNames.empty())
{
esm.startSubRecord("SCVR");
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
{
esm.writeHCString(*it);
}
esm.endRecord("SCVR");
}
esm.startSubRecord("SCDT");
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
esm.endRecord("SCDT");
esm.writeHNOString("SCTX", mScriptText);
}
void Script::blank() void Script::blank()
{ {

View file

@ -53,6 +53,9 @@ public:
void blank(); void blank();
///< Set record to default state (does not touch the ID/index). ///< Set record to default state (does not touch the ID/index).
private:
void loadSCVR(ESMReader &esm);
}; };
} }
#endif #endif

View file

@ -129,23 +129,47 @@ namespace ESM
unsigned int Skill::sRecordId = REC_SKIL; unsigned int Skill::sRecordId = REC_SKIL;
void Skill::load(ESMReader &esm) void Skill::load(ESMReader &esm)
{ {
esm.getHNT(mIndex, "INDX"); bool hasIndex = false;
esm.getHNT(mData, "SKDT", 24); bool hasData = false;
mDescription = esm.getHNOString("DESC"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'I','N','D','X'>::value:
esm.getHT(mIndex);
hasIndex = true;
break;
case ESM::FourCC<'S','K','D','T'>::value:
esm.getHT(mData, 24);
hasData = true;
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasIndex)
esm.fail("Missing INDX");
if (!hasData)
esm.fail("Missing SKDT");
// create an ID from the index and the name (only used in the editor and likely to change in the // create an ID from the index and the name (only used in the editor and likely to change in the
// future) // future)
mId = indexToId (mIndex); mId = indexToId (mIndex);
} }
void Skill::save(ESMWriter &esm) const void Skill::save(ESMWriter &esm) const
{ {
esm.writeHNT("INDX", mIndex); esm.writeHNT("INDX", mIndex);
esm.writeHNT("SKDT", mData, 24); esm.writeHNT("SKDT", mData, 24);
esm.writeHNOString("DESC", mDescription); esm.writeHNOString("DESC", mDescription);
} }
void Skill::blank() void Skill::blank()
{ {

View file

@ -8,19 +8,38 @@ namespace ESM
{ {
unsigned int SoundGenerator::sRecordId = REC_SNDG; unsigned int SoundGenerator::sRecordId = REC_SNDG;
void SoundGenerator::load(ESMReader &esm) void SoundGenerator::load(ESMReader &esm)
{ {
esm.getHNT(mType, "DATA", 4); bool hasData = false;
while (esm.hasMoreSubs())
mCreature = esm.getHNOString("CNAM"); {
mSound = esm.getHNOString("SNAM"); esm.getSubName();
} uint32_t name = esm.retSubName().val;
void SoundGenerator::save(ESMWriter &esm) const switch (name)
{ {
esm.writeHNT("DATA", mType, 4); case ESM::FourCC<'D','A','T','A'>::value:
esm.writeHNOCString("CNAM", mCreature); esm.getHT(mType, 4);
esm.writeHNOCString("SNAM", mSound); hasData = true;
} break;
case ESM::FourCC<'C','N','A','M'>::value:
mCreature = esm.getHString();
break;
case ESM::FourCC<'S','N','A','M'>::value:
mSound = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
}
void SoundGenerator::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mType, 4);
esm.writeHNOCString("CNAM", mCreature);
esm.writeHNOCString("SNAM", mSound);
}
void SoundGenerator::blank() void SoundGenerator::blank()
{ {

View file

@ -8,22 +8,35 @@ namespace ESM
{ {
unsigned int Sound::sRecordId = REC_SOUN; unsigned int Sound::sRecordId = REC_SOUN;
void Sound::load(ESMReader &esm) void Sound::load(ESMReader &esm)
{ {
mSound = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "DATA", 3); while (esm.hasMoreSubs())
/* {
cout << "vol=" << (int)data.volume esm.getSubName();
<< " min=" << (int)data.minRange uint32_t name = esm.retSubName().val;
<< " max=" << (int)data.maxRange switch (name)
<< endl; {
*/ case ESM::FourCC<'F','N','A','M'>::value:
} mSound = esm.getHString();
void Sound::save(ESMWriter &esm) const break;
{ case ESM::FourCC<'D','A','T','A'>::value:
esm.writeHNOCString("FNAM", mSound); esm.getHT(mData, 3);
esm.writeHNT("DATA", mData, 3); hasData = true;
} break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
}
void Sound::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mSound);
esm.writeHNT("DATA", mData, 3);
}
void Sound::blank() void Sound::blank()
{ {

View file

@ -8,40 +8,41 @@ namespace ESM
{ {
unsigned int Spell::sRecordId = REC_SPEL; unsigned int Spell::sRecordId = REC_SPEL;
void Spell::load(ESMReader &esm) void Spell::load(ESMReader &esm)
{
bool hasData = false;
while (esm.hasMoreSubs())
{ {
esm.getSubName(); mEffects.mList.clear();
uint32_t val = esm.retSubName().val; bool hasData = false;
while (esm.hasMoreSubs())
switch (val)
{ {
case ESM::FourCC<'F','N','A','M'>::value: esm.getSubName();
mName = esm.getHString(); uint32_t val = esm.retSubName().val;
break;
case ESM::FourCC<'S','P','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
ENAMstruct s;
esm.getHT(s, 24);
mEffects.mList.push_back(s);
break;
}
}
if (!hasData)
esm.fail("Missing SPDT subrecord");
}
void Spell::save(ESMWriter &esm) const switch (val)
{ {
esm.writeHNOCString("FNAM", mName); case ESM::FourCC<'F','N','A','M'>::value:
esm.writeHNT("SPDT", mData, 12); mName = esm.getHString();
mEffects.save(esm); break;
} case ESM::FourCC<'S','P','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
ENAMstruct s;
esm.getHT(s, 24);
mEffects.mList.push_back(s);
break;
}
}
if (!hasData)
esm.fail("Missing SPDT subrecord");
}
void Spell::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("SPDT", mData, 12);
mEffects.save(esm);
}
void Spell::blank() void Spell::blank()
{ {

View file

@ -8,15 +8,37 @@ namespace ESM
{ {
unsigned int StartScript::sRecordId = REC_SSCR; unsigned int StartScript::sRecordId = REC_SSCR;
void StartScript::load(ESMReader &esm) void StartScript::load(ESMReader &esm)
{ {
mData = esm.getHNString("DATA"); bool hasData = false;
mScript = esm.getHNString("NAME"); bool hasName = false;
} while (esm.hasMoreSubs())
void StartScript::save(ESMWriter &esm) const {
{ esm.getSubName();
esm.writeHNString("DATA", mData); uint32_t name = esm.retSubName().val;
esm.writeHNString("NAME", mScript); switch (name)
} {
case ESM::FourCC<'D','A','T','A'>::value:
mData = esm.getHString();
hasData = true;
break;
case ESM::FourCC<'N','A','M','E'>::value:
mScript = esm.getHString();
hasName = true;
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
if (!hasName)
esm.fail("Missing NAME");
}
void StartScript::save(ESMWriter &esm) const
{
esm.writeHNString("DATA", mData);
esm.writeHNString("NAME", mScript);
}
} }

View file

@ -8,15 +8,16 @@ namespace ESM
{ {
unsigned int Static::sRecordId = REC_STAT; unsigned int Static::sRecordId = REC_STAT;
void Static::load(ESMReader &esm) void Static::load(ESMReader &esm)
{ {
mPersistent = esm.getRecordFlags() & 0x0400; mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
} mModel = esm.getHNString("MODL");
void Static::save(ESMWriter &esm) const }
{ void Static::save(ESMWriter &esm) const
esm.writeHNCString("MODL", mModel); {
} esm.writeHNCString("MODL", mModel);
}
void Static::blank() void Static::blank()
{ {

View file

@ -50,8 +50,8 @@ namespace ESM
NAME32 mPlayerName; NAME32 mPlayerName;
}; };
GMDT mGameData; // Used in .ess savegames only GMDT mGameData; // Used in .ess savegames only
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, screenshot? std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot? std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
Data mData; Data mData;
int mFormat; int mFormat;

View file

@ -5,14 +5,6 @@
namespace ESM { namespace ESM {
void SpellList::load(ESMReader &esm)
{
mList.clear();
while (esm.isNextSub("NPCS")) {
add(esm);
}
}
void SpellList::add(ESMReader &esm) void SpellList::add(ESMReader &esm)
{ {
mList.push_back(esm.getHString()); mList.push_back(esm.getHString());

View file

@ -11,6 +11,7 @@ namespace ESM
/** A list of references to spells and spell effects. This is shared /** A list of references to spells and spell effects. This is shared
between the records BSGN, NPC and RACE. between the records BSGN, NPC and RACE.
NPCS subrecord.
*/ */
struct SpellList struct SpellList
{ {
@ -22,9 +23,6 @@ namespace ESM
/// Load one spell, assumes the subrecord name was already read /// Load one spell, assumes the subrecord name was already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// Load all spells
/// TODO: remove this method, the ESM format doesn't guarantee that all spell subrecords follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };
} }

View file

@ -152,9 +152,9 @@ namespace Gui
eventWidgetSelected(_sender); eventWidgetSelected(_sender);
} }
MyGUI::Widget* MWList::getItemWidget(const std::string& name) MyGUI::Button *MWList::getItemWidget(const std::string& name)
{ {
return mScrollView->findWidget (getName() + "_item_" + name); return mScrollView->findWidget (getName() + "_item_" + name)->castType<MyGUI::Button>();
} }
} }

View file

@ -43,7 +43,7 @@ namespace Gui
std::string getItemNameAt(unsigned int at); ///< \attention if there are separators, this method will return "" at the place where the separator is std::string getItemNameAt(unsigned int at); ///< \attention if there are separators, this method will return "" at the place where the separator is
void clear(); void clear();
MyGUI::Widget* getItemWidget(const std::string& name); MyGUI::Button* getItemWidget(const std::string& name);
///< get widget for an item name, useful to set up tooltip ///< get widget for an item name, useful to set up tooltip
virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void setPropertyOverride(const std::string& _key, const std::string& _value);

View file

@ -1,8 +1,8 @@
particle_system openmw/Ripples particle_system openmw/Ripples
{ {
material openmw/Ripple material openmw/Ripple
particle_width 50 particle_width 30
particle_height 50 particle_height 30
// To make the particles move with the scene node when the waterlevel changes // To make the particles move with the scene node when the waterlevel changes
local_space true local_space true
quota 300 quota 300
@ -17,7 +17,7 @@ particle_system openmw/Ripples
affector Scaler affector Scaler
{ {
rate 100 rate 120
} }
affector Rotator affector Rotator

View file

@ -28,9 +28,9 @@
<State name="normal" colour="#{fontcolour=journal_topic}" shift="0"/> <State name="normal" colour="#{fontcolour=journal_topic}" shift="0"/>
<State name="highlighted" colour="#{fontcolour=journal_topic_over}" shift="0"/> <State name="highlighted" colour="#{fontcolour=journal_topic_over}" shift="0"/>
<State name="pushed" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="pushed" colour="#{fontcolour=journal_topic_pressed}" shift="0"/>
<State name="normal_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="normal_checked" colour="0.2 0.2 0.2" shift="0"/>
<State name="highlighted_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="highlighted_checked" colour="0.4 0.4 0.4" shift="0"/>
<State name="pushed_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="pushed_checked" colour="0.5 0.5 0.5" shift="0"/>
</BasisSkin> </BasisSkin>
</Resource> </Resource>
</MyGUI> </MyGUI>