mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 15:26:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			625 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			625 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef OPENMW_ESSIMPORT_CONVERTER_H
 | |
| #define OPENMW_ESSIMPORT_CONVERTER_H
 | |
| 
 | |
| #include <limits>
 | |
| 
 | |
| #include <osg/Image>
 | |
| #include <osg/ref_ptr>
 | |
| 
 | |
| #include <components/esm/esmreader.hpp>
 | |
| #include <components/esm/esmwriter.hpp>
 | |
| 
 | |
| #include <components/esm/loadcell.hpp>
 | |
| #include <components/esm/loadbook.hpp>
 | |
| #include <components/esm/loadclas.hpp>
 | |
| #include <components/esm/loadglob.hpp>
 | |
| #include <components/esm/cellstate.hpp>
 | |
| #include <components/esm/loadfact.hpp>
 | |
| #include <components/esm/dialoguestate.hpp>
 | |
| #include <components/esm/custommarkerstate.hpp>
 | |
| #include <components/esm/loadcrea.hpp>
 | |
| #include <components/esm/weatherstate.hpp>
 | |
| #include <components/esm/globalscript.hpp>
 | |
| #include <components/esm/queststate.hpp>
 | |
| #include <components/esm/stolenitems.hpp>
 | |
| #include <components/esm/projectilestate.hpp>
 | |
| 
 | |
| #include "importcrec.hpp"
 | |
| #include "importcntc.hpp"
 | |
| 
 | |
| #include "importercontext.hpp"
 | |
| #include "importcellref.hpp"
 | |
| #include "importklst.hpp"
 | |
| #include "importgame.hpp"
 | |
| #include "importinfo.hpp"
 | |
| #include "importdial.hpp"
 | |
| #include "importques.hpp"
 | |
| #include "importjour.hpp"
 | |
| #include "importscpt.hpp"
 | |
| #include "importproj.h"
 | |
| #include "importsplm.h"
 | |
| 
 | |
| #include "convertacdt.hpp"
 | |
| #include "convertnpcc.hpp"
 | |
| #include "convertscpt.hpp"
 | |
| #include "convertplayer.hpp"
 | |
| 
 | |
| namespace ESSImport
 | |
| {
 | |
| 
 | |
| class Converter
 | |
| {
 | |
| public:
 | |
|     /// @return the order for writing this converter's records to the output file, in relation to other converters
 | |
|     virtual int getStage() { return 1; }
 | |
| 
 | |
|     virtual ~Converter() {}
 | |
| 
 | |
|     void setContext(Context& context) { mContext = &context; }
 | |
| 
 | |
|     /// @note The load method of ESM records accept the deleted flag as a parameter.
 | |
|     /// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.
 | |
|     virtual void read(ESM::ESMReader& esm)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     /// Called after the input file has been read in completely, which may be necessary
 | |
|     /// if the conversion process relies on information in other records
 | |
|     virtual void write(ESM::ESMWriter& esm)
 | |
|     {
 | |
| 
 | |
|     }
 | |
| 
 | |
| protected:
 | |
|     Context* mContext;
 | |
| };
 | |
| 
 | |
| /// Default converter: simply reads the record and writes it unmodified to the output
 | |
| template <typename T>
 | |
| class DefaultConverter : public Converter
 | |
| {
 | |
| public:
 | |
|     int getStage() override { return 0; }
 | |
| 
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         T record;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         record.load(esm, isDeleted);
 | |
|         mRecords[record.mId] = record;
 | |
|     }
 | |
| 
 | |
|     void write(ESM::ESMWriter& esm) override
 | |
|     {
 | |
|         for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
 | |
|         {
 | |
|             esm.startRecord(T::sRecordId);
 | |
|             it->second.save(esm);
 | |
|             esm.endRecord(T::sRecordId);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| protected:
 | |
|     std::map<std::string, T> mRecords;
 | |
| };
 | |
| 
 | |
| class ConvertNPC : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         ESM::NPC npc;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         npc.load(esm, isDeleted);
 | |
|         if (npc.mId != "player")
 | |
|         {
 | |
|             // Handles changes to the NPC struct, but since there is no index here
 | |
|             // it will apply to ALL instances of the class. seems to be the reason for the
 | |
|             // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
 | |
|             mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
 | |
|             mContext->mPlayerBase = npc;
 | |
|             ESM::SpellState::SpellParams empty;
 | |
|             // FIXME: player start spells and birthsign spells aren't listed here,
 | |
|             // need to fix openmw to account for this
 | |
|             for (const auto & spell : npc.mSpells.mList)
 | |
|                 mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
 | |
| 
 | |
|             // Clear the list now that we've written it, this prevents issues cropping up with
 | |
|             // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
 | |
|             mContext->mPlayerBase.mSpells.mList.clear();
 | |
| 
 | |
|             // Same with inventory. Actually it's strange this would contain something, since there's already an
 | |
|             // inventory list in NPCC. There seems to be a fair amount of redundancy in this format.
 | |
|             mContext->mPlayerBase.mInventory.mList.clear();
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertCREA : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         // See comment in ConvertNPC
 | |
|         ESM::Creature creature;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         creature.load(esm, isDeleted);
 | |
|         mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;
 | |
|     }
 | |
| };
 | |
| 
 | |
| // Do we need ConvertCONT?
 | |
| // I've seen a CONT record in a certain save file, but the container contents in it
 | |
| // were identical to a corresponding CNTC record. See previous comment about redundancy...
 | |
| 
 | |
| class ConvertGlobal : public DefaultConverter<ESM::Global>
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         ESM::Global global;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         global.load(esm, isDeleted);
 | |
|         if (Misc::StringUtils::ciEqual(global.mId, "gamehour"))
 | |
|             mContext->mHour = global.mValue.getFloat();
 | |
|         if (Misc::StringUtils::ciEqual(global.mId, "day"))
 | |
|             mContext->mDay = global.mValue.getInteger();
 | |
|         if (Misc::StringUtils::ciEqual(global.mId, "month"))
 | |
|             mContext->mMonth = global.mValue.getInteger();
 | |
|         if (Misc::StringUtils::ciEqual(global.mId, "year"))
 | |
|             mContext->mYear = global.mValue.getInteger();
 | |
|         mRecords[global.mId] = global;
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertClass : public DefaultConverter<ESM::Class>
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         ESM::Class class_;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         class_.load(esm, isDeleted);
 | |
|         if (class_.mId == "NEWCLASSID_CHARGEN")
 | |
|             mContext->mCustomPlayerClassName = class_.mName;
 | |
| 
 | |
|         mRecords[class_.mId] = class_;
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertBook : public DefaultConverter<ESM::Book>
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         ESM::Book book;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         book.load(esm, isDeleted);
 | |
|         if (book.mData.mSkillId == -1)
 | |
|             mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
 | |
| 
 | |
|         mRecords[book.mId] = book;
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertNPCC : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         std::string id = esm.getHNString("NAME");
 | |
|         NPCC npcc;
 | |
|         npcc.load(esm);
 | |
|         if (id == "PlayerSaveGame")
 | |
|         {
 | |
|             convertNPCC(npcc, mContext->mPlayer.mObject);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             int index = npcc.mNPDT.mIndex;
 | |
|             mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc));
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertREFR : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         REFR refr;
 | |
|         refr.load(esm);
 | |
|         assert(refr.mRefID == "PlayerSaveGame");
 | |
|         mContext->mPlayer.mObject.mPosition = refr.mPos;
 | |
| 
 | |
|         ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats;
 | |
|         convertACDT(refr.mActorData.mACDT, cStats);
 | |
| 
 | |
|         ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats;
 | |
|         convertNpcData(refr.mActorData, npcStats);
 | |
| 
 | |
|         mSelectedSpell = refr.mActorData.mSelectedSpell;
 | |
|         if (!refr.mActorData.mSelectedEnchantItem.empty())
 | |
|         {
 | |
|             ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
 | |
| 
 | |
|             for (unsigned int i=0; i<invState.mItems.size(); ++i)
 | |
|             {
 | |
|                 // FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
 | |
|                 if (Misc::StringUtils::ciEqual(invState.mItems[i].mRef.mRefID, refr.mActorData.mSelectedEnchantItem))
 | |
|                     invState.mSelectedEnchantItem = i;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     void write(ESM::ESMWriter& esm) override
 | |
|     {
 | |
|         esm.startRecord(ESM::REC_ASPL);
 | |
|         esm.writeHNString("ID__", mSelectedSpell);
 | |
|         esm.endRecord(ESM::REC_ASPL);
 | |
|     }
 | |
| private:
 | |
|     std::string mSelectedSpell;
 | |
| };
 | |
| 
 | |
| class ConvertPCDT : public Converter
 | |
| {
 | |
| public:
 | |
|     ConvertPCDT()
 | |
|         : mFirstPersonCam(true),
 | |
|           mTeleportingEnabled(true),
 | |
|           mLevitationEnabled(true)
 | |
|     {}
 | |
| 
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         PCDT pcdt;
 | |
|         pcdt.load(esm);
 | |
| 
 | |
|         convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
 | |
|     }
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         esm.startRecord(ESM::REC_ENAB);
 | |
|         esm.writeHNT("TELE", mTeleportingEnabled);
 | |
|         esm.writeHNT("LEVT", mLevitationEnabled);
 | |
|         esm.endRecord(ESM::REC_ENAB);
 | |
| 
 | |
|         esm.startRecord(ESM::REC_CAM_);
 | |
|         esm.writeHNT("FIRS", mFirstPersonCam);
 | |
|         esm.endRecord(ESM::REC_CAM_);
 | |
|     }
 | |
| private:
 | |
|     bool mFirstPersonCam;
 | |
|     bool mTeleportingEnabled;
 | |
|     bool mLevitationEnabled;
 | |
| };
 | |
| 
 | |
| class ConvertCNTC : public Converter
 | |
| {
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         std::string id = esm.getHNString("NAME");
 | |
|         CNTC cntc;
 | |
|         cntc.load(esm);
 | |
|         mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc));
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertCREC : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         std::string id = esm.getHNString("NAME");
 | |
|         CREC crec;
 | |
|         crec.load(esm);
 | |
|         mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertFMAP : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override;
 | |
|     void write(ESM::ESMWriter &esm) override;
 | |
| 
 | |
| private:
 | |
|     osg::ref_ptr<osg::Image> mGlobalMapImage;
 | |
| };
 | |
| 
 | |
| class ConvertCell : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override;
 | |
|     void write(ESM::ESMWriter& esm) override;
 | |
| 
 | |
| private:
 | |
|     struct Cell
 | |
|     {
 | |
|         ESM::Cell mCell;
 | |
|         std::vector<CellRef> mRefs;
 | |
|         std::vector<unsigned int> mFogOfWar;
 | |
|     };
 | |
| 
 | |
|     std::map<std::string, Cell> mIntCells;
 | |
|     std::map<std::pair<int, int>, Cell> mExtCells;
 | |
| 
 | |
|     std::vector<ESM::CustomMarker> mMarkers;
 | |
| 
 | |
|     void writeCell(const Cell& cell, ESM::ESMWriter &esm);
 | |
| };
 | |
| 
 | |
| class ConvertKLST : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         KLST klst;
 | |
|         klst.load(esm);
 | |
|         mKillCounter = klst.mKillCounter;
 | |
| 
 | |
|         mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
 | |
|     }
 | |
| 
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         esm.startRecord(ESM::REC_DCOU);
 | |
|         for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
 | |
|         {
 | |
|             esm.writeHNString("ID__", it->first);
 | |
|             esm.writeHNT ("COUN", it->second);
 | |
|         }
 | |
|         esm.endRecord(ESM::REC_DCOU);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     std::map<std::string, int> mKillCounter;
 | |
| };
 | |
| 
 | |
| class ConvertFACT : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         ESM::Faction faction;
 | |
|         bool isDeleted = false;
 | |
| 
 | |
|         faction.load(esm, isDeleted);
 | |
|         std::string id = Misc::StringUtils::lowerCase(faction.mId);
 | |
| 
 | |
|         for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
 | |
|         {
 | |
|             std::string faction2 = Misc::StringUtils::lowerCase(it->first);
 | |
|             mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| /// Stolen items
 | |
| class ConvertSTLN : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         std::string itemid = esm.getHNString("NAME");
 | |
|         Misc::StringUtils::lowerCaseInPlace(itemid);
 | |
| 
 | |
|         while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
 | |
|         {
 | |
|             if (esm.retSubName().toString() == "FNAM")
 | |
|             {
 | |
|                 std::string factionid = esm.getHString();
 | |
|                 mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 std::string ownerid = esm.getHString();
 | |
|                 mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         ESM::StolenItems items;
 | |
|         for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
 | |
|         {
 | |
|             std::map<std::pair<std::string, bool>, int> owners;
 | |
|             for (const auto & ownerIt : it->second)
 | |
|             {
 | |
|                 owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second)
 | |
|                                              // Since OpenMW doesn't suffer from the owner contamination bug,
 | |
|                                              // it needs a count argument. But for legacy savegames, we don't know
 | |
|                                              // this count, so must assume all items of that ID are stolen,
 | |
|                                              // like vanilla MW did.
 | |
|                                              ,std::numeric_limits<int>::max()));
 | |
|             }
 | |
| 
 | |
|             items.mStolenItems.insert(std::make_pair(it->first, owners));
 | |
|         }
 | |
| 
 | |
|         esm.startRecord(ESM::REC_STLN);
 | |
|         items.write(esm);
 | |
|         esm.endRecord(ESM::REC_STLN);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     typedef std::pair<std::string, bool> Owner; // <owner id, bool isFaction>
 | |
| 
 | |
|     std::map<std::string, std::set<Owner> > mStolenItems;
 | |
| };
 | |
| 
 | |
| /// Seen responses for a dialogue topic?
 | |
| /// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs
 | |
| /// Dialogue conversion problems:
 | |
| /// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID.
 | |
| /// - Seen dialogue responses only store the INFO id, rather than the fulltext.
 | |
| /// - Quest stages only store the INFO id, rather than the journal entry fulltext.
 | |
| class ConvertINFO : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         INFO info;
 | |
|         info.load(esm);
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertDIAL : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         std::string id = esm.getHNString("NAME");
 | |
|         DIAL dial;
 | |
|         dial.load(esm);
 | |
|         if (dial.mIndex > 0)
 | |
|             mDials[id] = dial;
 | |
|     }
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
 | |
|         {
 | |
|             esm.startRecord(ESM::REC_QUES);
 | |
|             ESM::QuestState state;
 | |
|             state.mFinished = 0;
 | |
|             state.mState = it->second.mIndex;
 | |
|             state.mTopic = Misc::StringUtils::lowerCase(it->first);
 | |
|             state.save(esm);
 | |
|             esm.endRecord(ESM::REC_QUES);
 | |
|         }
 | |
|     }
 | |
| private:
 | |
|     std::map<std::string, DIAL> mDials;
 | |
| };
 | |
| 
 | |
| class ConvertQUES : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         std::string id = esm.getHNString("NAME");
 | |
|         QUES quest;
 | |
|         quest.load(esm);
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertJOUR : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override
 | |
|     {
 | |
|         JOUR journal;
 | |
|         journal.load(esm);
 | |
|     }
 | |
| };
 | |
| 
 | |
| class ConvertGAME : public Converter
 | |
| {
 | |
| public:
 | |
|     ConvertGAME()
 | |
|         : mHasGame(false)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         mGame.load(esm);
 | |
|         mHasGame = true;
 | |
|     }
 | |
| 
 | |
|     int validateWeatherID(int weatherID)
 | |
|     {
 | |
|         if(weatherID >= -1 && weatherID < 10)
 | |
|         {
 | |
|             return weatherID;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             std::stringstream error;
 | |
|             error << "Invalid weather ID:" << weatherID << std::endl;
 | |
|             throw std::runtime_error(error.str());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         if (!mHasGame)
 | |
|             return;
 | |
|         esm.startRecord(ESM::REC_WTHR);
 | |
|         ESM::WeatherState weather;
 | |
|         weather.mTimePassed = 0.0f;
 | |
|         weather.mFastForward = false;
 | |
|         weather.mWeatherUpdateTime = mGame.mGMDT.mTimeOfNextTransition - mContext->mHour;
 | |
|         weather.mTransitionFactor = 1 - (mGame.mGMDT.mWeatherTransition / 100.0f);
 | |
|         weather.mCurrentWeather = validateWeatherID(mGame.mGMDT.mCurrentWeather);
 | |
|         weather.mNextWeather = validateWeatherID(mGame.mGMDT.mNextWeather);
 | |
|         weather.mQueuedWeather = -1;
 | |
|         // TODO: Determine how ModRegion modifiers are saved in Morrowind.
 | |
|         weather.save(esm);
 | |
|         esm.endRecord(ESM::REC_WTHR);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     bool mHasGame;
 | |
|     GAME mGame;
 | |
| };
 | |
| 
 | |
| /// Running global script
 | |
| class ConvertSCPT : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader &esm) override
 | |
|     {
 | |
|         SCPT script;
 | |
|         script.load(esm);
 | |
|         ESM::GlobalScript out;
 | |
|         convertSCPT(script, out);
 | |
|         mScripts.push_back(out);
 | |
|     }
 | |
|     void write(ESM::ESMWriter &esm) override
 | |
|     {
 | |
|         for (const auto & script : mScripts)
 | |
|         {
 | |
|             esm.startRecord(ESM::REC_GSCR);
 | |
|             script.save(esm);
 | |
|             esm.endRecord(ESM::REC_GSCR);
 | |
|         }
 | |
|     }
 | |
| private:
 | |
|     std::vector<ESM::GlobalScript> mScripts;
 | |
| };
 | |
| 
 | |
| /// Projectile converter
 | |
| class ConvertPROJ : public Converter
 | |
| {
 | |
| public:
 | |
|     int getStage() override { return 2; }
 | |
|     void read(ESM::ESMReader& esm) override;
 | |
|     void write(ESM::ESMWriter& esm) override;
 | |
| private:
 | |
|     void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
 | |
|     PROJ mProj;
 | |
| };
 | |
| 
 | |
| class ConvertSPLM : public Converter
 | |
| {
 | |
| public:
 | |
|     void read(ESM::ESMReader& esm) override;
 | |
|     void write(ESM::ESMWriter& esm) override;
 | |
| private:
 | |
|     SPLM mSPLM;
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| #endif
 |