1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 18:35:20 +00:00

Merge remote-tracking branch 'scrawl/master'

This commit is contained in:
Marc Zinnschlag 2015-01-29 16:12:29 +01:00
commit 1150b41c18
91 changed files with 727 additions and 567 deletions

View File

@ -1,9 +1,7 @@
OpenMW
======
[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
[![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.

View File

@ -59,6 +59,7 @@ struct Arguments
std::string outname;
std::vector<std::string> types;
std::string name;
ESMData data;
ESM::ESMReader reader;
@ -78,6 +79,8 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May "
"be specified multiple times. Only affects dump mode.")
("name,n", bpo::value<std::string>(),
"Show only the record with this name. Only affects dump mode.")
("plain,p", "Print contents of dialogs, books and scripts. "
"(skipped by default)"
"Only affects dump mode.")
@ -148,7 +151,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
}
if (variables.count("type") > 0)
info.types = variables["type"].as< std::vector<std::string> >();
info.types = variables["type"].as< std::vector<std::string> >();
if (variables.count("name") > 0)
info.name = variables["name"].as<std::string>();
info.mode = variables["mode"].as<std::string>();
if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
@ -265,6 +270,8 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
if (!ref.mKey.empty())
std::cout << " Key: '" << ref.mKey << "'" << std::endl;
}
}
@ -358,6 +365,9 @@ int load(Arguments& info)
if (id.empty())
id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
interested = false;
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n";
@ -385,7 +395,7 @@ int load(Arguments& info)
record->load(esm);
if (!quiet && interested) record->print();
if (record->getType().val == ESM::REC_CELL && loadCells) {
if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
}

View File

@ -2,6 +2,8 @@
#include "labels.hpp"
#include <iostream>
#include <sstream>
#include <boost/format.hpp>
void printAIPackage(ESM::AIPackage p)
@ -752,7 +754,7 @@ void Record<ESM::DialInfo>::print()
if (mData.mCell != "")
std::cout << " Cell: " << mData.mCell << std::endl;
if (mData.mData.mDisposition > 0)
std::cout << " Disposition: " << mData.mData.mDisposition << std::endl;
std::cout << " Disposition/Journal index: " << mData.mData.mDisposition << std::endl;
if (mData.mData.mGender != ESM::DialInfo::NA)
std::cout << " Gender: " << mData.mData.mGender << std::endl;
if (mData.mSound != "")
@ -812,7 +814,6 @@ void Record<ESM::Land>::print()
{
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " HasData: " << mData.mHasData << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// Seems like this should done with reference counting in the

View File

@ -24,6 +24,7 @@ set(ESSIMPORTER_FILES
convertcrec.cpp
convertcntc.cpp
convertscri.cpp
convertscpt.cpp
)
add_executable(openmw-essimporter

View File

@ -3,6 +3,7 @@
#include <stdexcept>
#include <OgreImage.h>
#include <OgreColourValue.h>
#include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
@ -69,7 +70,65 @@ namespace ESSImport
esm.getSubHeader();
data.resize(esm.getSubSize());
esm.getExact(&data[0], data.size());
convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga");
Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(&data[0], data.size()));
mGlobalMapImage.loadRawData(stream, maph.size, maph.size, 1, Ogre::PF_BYTE_RGB);
// to match openmw size
mGlobalMapImage.resize(maph.size*2, maph.size*2, Ogre::Image::FILTER_BILINEAR);
}
void ConvertFMAP::write(ESM::ESMWriter &esm)
{
int numcells = mGlobalMapImage.getWidth() / 18; // NB truncating, doesn't divide perfectly
// with the 512x512 map the game has by default
int cellSize = mGlobalMapImage.getWidth()/numcells;
// Note the upper left corner of the (0,0) cell should be at (width/2, height/2)
mContext->mGlobalMapState.mBounds.mMinX = -numcells/2;
mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2;
mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2;
mContext->mGlobalMapState.mBounds.mMaxY = numcells/2;
Ogre::Image image2;
std::vector<Ogre::uint8> data;
int width = cellSize*numcells;
int height = cellSize*numcells;
data.resize(width*height*4, 0);
image2.loadDynamicImage(&data[0], width, height, Ogre::PF_BYTE_RGBA);
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it)
{
if (it->first > mContext->mGlobalMapState.mBounds.mMaxX
|| it->first < mContext->mGlobalMapState.mBounds.mMinX
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY
|| it->second < mContext->mGlobalMapState.mBounds.mMinY)
{
// out of bounds, I think this could happen, since the original engine had a fixed-size map
continue;
}
int imageLeftSrc = mGlobalMapImage.getWidth()/2;
int imageTopSrc = mGlobalMapImage.getHeight()/2;
imageLeftSrc += it->first * cellSize;
imageTopSrc -= it->second * cellSize;
int imageLeftDst = width/2;
int imageTopDst = height/2;
imageLeftDst += it->first * cellSize;
imageTopDst -= it->second * cellSize;
for (int x=0; x<cellSize; ++x)
for (int y=0; y<cellSize; ++y)
image2.setColourAt(mGlobalMapImage.getColourAt(imageLeftSrc+x, imageTopSrc+y, 0)
, imageLeftDst+x, imageTopDst+y, 0);
}
Ogre::DataStreamPtr encoded = image2.encode("png");
mContext->mGlobalMapState.mImageData.resize(encoded->size());
encoded->read(&mContext->mGlobalMapState.mImageData[0], encoded->size());
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
}
void ConvertCell::read(ESM::ESMReader &esm)
@ -103,6 +162,10 @@ namespace ESSImport
// (probably offset of that specific fog texture?)
while (esm.isNextSub("NAM8"))
{
if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored.
// are there any flags marking explored cells?
mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY));
esm.getSubHeader();
if (esm.getSubSize() == 36)
@ -313,10 +376,6 @@ namespace ESSImport
it->save(esm);
esm.endRecord(ESM::REC_MARK);
}
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
}
}

View File

@ -1,6 +1,8 @@
#ifndef OPENMW_ESSIMPORT_CONVERTER_H
#define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -13,6 +15,9 @@
#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 "importcrec.hpp"
#include "importcntc.hpp"
@ -29,6 +34,7 @@
#include "convertacdt.hpp"
#include "convertnpcc.hpp"
#include "convertscpt.hpp"
namespace ESSImport
{
@ -206,7 +212,7 @@ public:
else
{
int index = npcc.mNPDT.mIndex;
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second;
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc));
}
}
};
@ -266,7 +272,7 @@ public:
faction.mExpelled = (it->mFlags & 0x2) != 0;
faction.mRank = it->mRank;
faction.mReputation = it->mReputation;
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction;
mContext->mPlayer.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
}
for (int i=0; i<8; ++i)
mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
@ -308,6 +314,10 @@ class ConvertFMAP : public Converter
{
public:
virtual void read(ESM::ESMReader &esm);
virtual void write(ESM::ESMWriter &esm);
private:
Ogre::Image mGlobalMapImage;
};
class ConvertCell : public Converter
@ -428,7 +438,24 @@ public:
std::string id = esm.getHNString("NAME");
DIAL dial;
dial.load(esm);
if (dial.mIndex > 0)
mDials[id] = dial;
}
virtual void write(ESM::ESMWriter &esm)
{
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
@ -455,11 +482,69 @@ public:
class ConvertGAME : public Converter
{
public:
ConvertGAME() : mHasGame(false) {}
std::string toString(int weatherId)
{
switch (weatherId)
{
case 0:
return "clear";
case 1:
return "cloudy";
case 2:
return "foggy";
case 3:
return "overcast";
case 4:
return "rain";
case 5:
return "thunderstorm";
case 6:
return "ashstorm";
case 7:
return "blight";
case 8:
return "snow";
case 9:
return "blizzard";
case -1:
return "";
default:
{
std::stringstream error;
error << "unknown weather id: " << weatherId;
throw std::runtime_error(error.str());
}
}
}
virtual void read(ESM::ESMReader &esm)
{
GAME game;
game.load(esm);
mGame.load(esm);
mHasGame = true;
}
virtual void write(ESM::ESMWriter &esm)
{
if (!mHasGame)
return;
esm.startRecord(ESM::REC_WTHR);
ESM::WeatherState weather;
weather.mCurrentWeather = toString(mGame.mGMDT.mCurrentWeather);
weather.mNextWeather = toString(mGame.mGMDT.mNextWeather);
weather.mRemainingTransitionTime = mGame.mGMDT.mWeatherTransition/100.f*(0.015*24*3600);
weather.mHour = mContext->mHour;
weather.mWindSpeed = 0.f;
weather.mTimePassed = 0.0;
weather.mFirstUpdate = false;
weather.save(esm);
esm.endRecord(ESM::REC_WTHR);
}
private:
bool mHasGame;
GAME mGame;
};
/// Running global script
@ -470,7 +555,21 @@ public:
{
SCPT script;
script.load(esm);
ESM::GlobalScript out;
convertSCPT(script, out);
mScripts.push_back(out);
}
virtual void write(ESM::ESMWriter &esm)
{
for (std::vector<ESM::GlobalScript>::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it)
{
esm.startRecord(ESM::REC_GSCR);
it->save(esm);
esm.endRecord(ESM::REC_GSCR);
}
}
private:
std::vector<ESM::GlobalScript> mScripts;
};
}

View File

@ -0,0 +1,17 @@
#include "convertscpt.hpp"
#include <components/misc/stringops.hpp>
#include "convertscri.hpp"
namespace ESSImport
{
void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out)
{
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
out.mRunning = scpt.mRunning;
convertSCRI(scpt.mSCRI, out.mLocals);
}
}

View File

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H
#define OPENMW_ESSIMPORT_CONVERTSCPT_H
#include <components/esm/globalscript.hpp>
#include "importscpt.hpp"
namespace ESSImport
{
void convertSCPT(const SCPT& scpt, ESM::GlobalScript& out);
}
#endif

View File

@ -60,10 +60,6 @@ namespace ESSImport
if (esm.isNextSub("PWPS"))
esm.skipHSub();
// unsure at which point between LSTN and CHRD
if (esm.isNextSub("APUD"))
esm.skipHSub(); // 40 bytes, starts with string "ancestor guardian". maybe spellcasting in progress?
if (esm.isNextSub("WNAM"))
{
std::string id = esm.getHString();
@ -77,6 +73,20 @@ namespace ESSImport
esm.skipHSub(); // 4 byte, 0
}
while (esm.isNextSub("APUD"))
{
// used power
esm.getSubHeader();
std::string id = esm.getString(32);
(void)id;
// timestamp can't be used: this is the total hours passed, calculated by
// timestamp = 24 * (365 * year + cumulativeDays[month] + day)
// unfortunately cumulativeDays[month] is not clearly defined,
// in the (non-MCP) vanilla version the first month was missing, but MCP added it.
double timestamp;
esm.getT(timestamp);
}
// FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int));

View File

@ -29,6 +29,9 @@ namespace ESSImport
ESM::DialogueState mDialogueState;
// cells which should show an explored overlay on the global map
std::set<std::pair<int, int> > mExploredCells;
ESM::GlobalMap mGlobalMapState;
int mDay, mMonth, mYear;

View File

@ -7,7 +7,23 @@ namespace ESSImport
void GAME::load(ESM::ESMReader &esm)
{
esm.getHNT(mGMDT, "GMDT");
esm.getSubNameIs("GMDT");
esm.getSubHeader();
if (esm.getSubSize() == 92)
{
esm.getExact(&mGMDT, 92);
mGMDT.mSecundaPhase = 0;
}
else if (esm.getSubSize() == 96)
{
esm.getT(mGMDT);
}
else
esm.fail("unexpected subrecord size for GAME.GMDT");
mGMDT.mWeatherTransition &= (0x000000ff);
mGMDT.mSecundaPhase &= (0x000000ff);
mGMDT.mMasserPhase &= (0x000000ff);
}
}

View File

@ -20,7 +20,7 @@ namespace ESSImport
int mCurrentWeather, mNextWeather;
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition
int masserPhase, secundaPhase; // top 3 bytes may be garbage
int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage
};
GMDT mGMDT;

View File

@ -13,8 +13,14 @@ namespace ESSImport
mSCRI.load(esm);
mRNAM = -1;
esm.getHNOT(mRNAM, "RNAM");
mRefNum = -1;
if (esm.isNextSub("RNAM"))
{
mRunning = true;
esm.getHT(mRefNum);
}
else
mRunning = false;
}
}

View File

@ -14,7 +14,6 @@ namespace ESSImport
{
// A running global script
// TODO: test how targeted scripts are saved
struct SCPT
{
ESM::Script::SCHD mSCHD;
@ -22,7 +21,8 @@ namespace ESSImport
// values of local variables
SCRI mSCRI;
int mRNAM; // unknown, seems to be -1 for some scripts, some huge integer for others
bool mRunning;
int mRefNum; // Targeted reference, -1: no reference
void load(ESM::ESMReader& esm);
};

View File

@ -453,6 +453,7 @@ namespace MWBase
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
virtual void reattachPlayerCamera() = 0;
/// \todo this does not belong here
virtual void frameStarted (float dt, bool paused) = 0;

View File

@ -296,25 +296,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
// NPC stats
if (!ref->mBase->mFaction.empty())
{
std::string faction = ref->mBase->mFaction;
if (const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().search(faction))
{
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt52.mRank);
}
else
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt12.mRank);
}
}
else
std::cerr << "Warning: ignoring nonexistent faction '" << faction << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
}
// creature stats
int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
@ -371,13 +352,13 @@ namespace MWClass
std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
}
if (data->mNpcStats.getFactionRanks().size())
if (!ref->mBase->mFaction.empty())
{
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt();
int rank = data->mNpcStats.getFactionRanks().begin()->second;
int rank = ref->mBase->getFactionRank();
data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
}
@ -1371,4 +1352,16 @@ namespace MWClass
{
return true;
}
std::string Npc::getPrimaryFaction (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->mFaction;
}
int Npc::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank();
}
}

View File

@ -189,6 +189,9 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const;
virtual std::string getPrimaryFaction(const MWWorld::Ptr &ptr) const;
virtual int getPrimaryFactionRank(const MWWorld::Ptr &ptr) const;
};
}

View File

@ -67,14 +67,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
if (isCreature)
return false;
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end())
if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction))
return false;
// check rank
if (iter->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
else if (info.mData.mRank != -1)
@ -83,13 +80,8 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
return false;
// Rank requirement, but no faction given. Use the actor's faction, if there is one.
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
if (!stats.getFactionRanks().size())
return false;
// check rank
if (stats.getFactionRanks().begin()->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
@ -336,12 +328,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_RankRequirement:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty())
return 0;
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
int rank = getFactionRank (player, faction);
if (rank>=9)
@ -376,15 +366,14 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_FactionRankDiff:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty())
return 0;
const std::pair<std::string, int> faction =
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
int rank = getFactionRank (player, faction.first);
return rank-faction.second;
int rank = getFactionRank (player, faction);
int npcRank = mActor.getClass().getPrimaryFactionRank(mActor);
return rank-npcRank;
}
case SelectWrapper::Function_WerewolfKills:
@ -396,11 +385,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{
bool low = select.getFunction()==SelectWrapper::Function_RankLow;
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0;
std::string factionId = mActor.getClass().getPrimaryFaction(mActor);
std::string factionId =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (factionId.empty())
return 0;
int value = 0;
@ -454,7 +442,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotFaction:
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mFaction, select.getName());
return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName());
case SelectWrapper::Function_NotClass:
@ -494,8 +482,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameFaction:
return mActor.getClass().getNpcStats (mActor).isSameFaction (
player.getClass().getNpcStats (player));
return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor));
case SelectWrapper::Function_PcCommonDisease:
@ -512,11 +499,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_PcExpelled:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return false;
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (faction.empty())
return false;
return player.getClass().getNpcStats(player).getExpelled(faction);
}
@ -561,7 +547,7 @@ int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::st
{
MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (factionId);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId));
if (iter==stats.getFactionRanks().end())
return -1;

View File

@ -89,7 +89,7 @@ namespace MWDialogue
for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mData.mDisposition==index) /// \todo cleanup info structure
if (iter->mData.mJournalIndex==index)
{
return iter->mId;
}

View File

@ -259,7 +259,12 @@ namespace MWDialogue
record.load (reader);
if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record));
{
std::pair<TQuestContainer::iterator, bool> result = mQuests.insert (std::make_pair (record.mTopic, record));
// reapply quest index, this is to handle users upgrading from only
// Morrowind.esm (no quest states) to Morrowind.esm + Tribunal.esm
result.first->second.setIndex(record.mState);
}
}
}
}

View File

@ -75,7 +75,7 @@ namespace MWDialogue
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == entry.mInfoId)
{
index = iter->mData.mDisposition; /// \todo cleanup info structure
index = iter->mData.mJournalIndex;
break;
}

View File

@ -5,14 +5,14 @@
namespace MWGui
{
void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool correct)
void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool stretch)
{
if (mChild)
{
MyGUI::Gui::getInstance().destroyWidget(mChild);
mChild = NULL;
}
if (correct)
if (!stretch)
{
setImageTexture("black.png");

View File

@ -18,9 +18,9 @@ namespace MWGui
/**
* @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions
* @param correct Add black bars?
* @param stretch Stretch to fill the whole screen, or add black bars?
*/
void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool correct=true);
void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true);
virtual void setSize (const MyGUI::IntSize &_value);
virtual void setCoord (const MyGUI::IntCoord &_value);

View File

@ -290,7 +290,8 @@ namespace MWGui
// Add the command to the history, and set the current pointer to
// the end of the list
mCommandHistory.push_back(cm);
if (mCommandHistory.empty() || mCommandHistory.back() != cm)
mCommandHistory.push_back(cm);
mCurrent = mCommandHistory.end();
mEditString.clear();

View File

@ -154,8 +154,6 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model.
setTitle(container.getClass().getName(container));
}

View File

@ -155,11 +155,6 @@ void ItemView::setSize(const MyGUI::IntSize &_value)
layoutWidgets();
}
void ItemView::setSize(int _width, int _height)
{
setSize(MyGUI::IntSize(_width, _height));
}
void ItemView::setCoord(const MyGUI::IntCoord &_value)
{
bool changed = (_value.width != getWidth() || _value.height != getHeight());
@ -168,11 +163,6 @@ void ItemView::setCoord(const MyGUI::IntCoord &_value)
layoutWidgets();
}
void ItemView::setCoord(int _left, int _top, int _width, int _height)
{
setCoord(MyGUI::IntCoord(_left, _top, _width, _height));
}
void ItemView::registerComponents()
{
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::ItemView>("Widget");

View File

@ -37,8 +37,6 @@ namespace MWGui
virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _value);
void setSize(int _width, int _height);
void setCoord(int _left, int _top, int _width, int _height);
void onSelectedItem (MyGUI::Widget* sender);
void onSelectedBackground (MyGUI::Widget* sender);

View File

@ -149,7 +149,9 @@ namespace MWGui
Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
// TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3
mBackgroundImage->setBackgroundImage(randomSplash, true, true);
// we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3
bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
mBackgroundImage->setBackgroundImage(randomSplash, true, stretch);
}
else
std::cerr << "No loading screens found!" << std::endl;

View File

@ -160,6 +160,8 @@ namespace MWGui
if (!show)
return;
bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
if (mHasAnimatedMenu)
{
if (!mVideo)
@ -180,16 +182,7 @@ namespace MWGui
int screenHeight = viewSize.height;
mVideoBackground->setSize(screenWidth, screenHeight);
if (mVideo->getVideoHeight() > 0)
{
double imageaspect = static_cast<double>(mVideo->getVideoWidth())/mVideo->getVideoHeight();
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
mVideo->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
}
mVideo->autoResize(stretch);
mVideo->setVisible(true);
}
@ -199,7 +192,7 @@ namespace MWGui
{
mBackground = MyGUI::Gui::getInstance().createWidgetReal<BackgroundImage>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Menu");
mBackground->setBackgroundImage("textures\\menu_morrowind.dds");
mBackground->setBackgroundImage("textures\\menu_morrowind.dds", true, stretch);
}
mBackground->setVisible(true);
}

View File

@ -201,11 +201,6 @@ namespace MWGui
layoutWidgets();
}
void SpellView::setSize(int _width, int _height)
{
setSize(MyGUI::IntSize(_width, _height));
}
void SpellView::setCoord(const MyGUI::IntCoord &_value)
{
bool changed = (_value.width != getWidth() || _value.height != getHeight());
@ -214,11 +209,6 @@ namespace MWGui
layoutWidgets();
}
void SpellView::setCoord(int _left, int _top, int _width, int _height)
{
setCoord(MyGUI::IntCoord(_left, _top, _width, _height));
}
void SpellView::adjustSpellWidget(const Spell &spell, SpellModel::ModelIndex index, MyGUI::Widget *widget)
{
if (spell.mType == Spell::Type_EnchantedItem)

View File

@ -45,8 +45,6 @@ namespace MWGui
virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _value);
void setSize(int _width, int _height);
void setCoord(int _left, int _top, int _width, int _height);
private:
MyGUI::ScrollView* mScrollView;

View File

@ -108,7 +108,6 @@ namespace MWGui
void StatsWindow::setPlayerName(const std::string& playerName)
{
mMainWidget->castType<MyGUI::Window>()->setCaption(playerName);
adjustWindowCaption();
}
void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value)

View File

@ -133,8 +133,6 @@ namespace MWGui
updateLabels();
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model.
setTitle(actor.getClass().getName(actor));
onFilterChanged(mFilterAll);

View File

@ -2,6 +2,8 @@
#include <extern/ogre-ffmpeg-videoplayer/videoplayer.hpp>
#include <MyGUI_RenderManager.h>
#include "../mwsound/movieaudiofactory.hpp"
namespace MWGui
@ -46,4 +48,24 @@ bool VideoWidget::hasAudioStream()
return mPlayer->hasAudioStream();
}
void VideoWidget::autoResize(bool stretch)
{
MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize();
if (getParent())
screenSize = getParent()->getSize();
if (getVideoHeight() > 0 && !stretch)
{
double imageaspect = static_cast<double>(getVideoWidth())/getVideoHeight();
int leftPadding = std::max(0.0, (screenSize.width - screenSize.height * imageaspect) / 2);
int topPadding = std::max(0.0, (screenSize.height - screenSize.width / imageaspect) / 2);
setCoord(leftPadding, topPadding,
screenSize.width - leftPadding*2, screenSize.height - topPadding*2);
}
else
setCoord(0,0,screenSize.width,screenSize.height);
}
}

View File

@ -35,6 +35,12 @@ namespace MWGui
/// Stop video and free resources (done automatically on destruction)
void stop();
/// Adjust the coordinates of this video widget relative to its parent,
/// based on the dimensions of the playing video.
/// @param stretch Stretch the video to fill the whole screen? If false,
/// black bars may be added to fix the aspect ratio.
void autoResize (bool stretch);
private:
std::auto_ptr<Video::VideoPlayer> mPlayer;
};

View File

@ -1712,18 +1712,9 @@ namespace MWGui
void WindowManager::sizeVideo(int screenWidth, int screenHeight)
{
// Use black bars to correct aspect ratio
bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
mVideoBackground->setSize(screenWidth, screenHeight);
if (mVideoWidget->getVideoHeight() > 0)
{
double imageaspect = static_cast<double>(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight();
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
mVideoWidget->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
}
mVideoWidget->autoResize(stretch);
}
WindowModal* WindowManager::getCurrentModal() const

View File

@ -58,7 +58,7 @@ namespace
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY)
{
if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH)
if((to - from).length() >= PATHFIND_CAUTION_DIST || std::abs(from.z - to.z) <= PATHFIND_Z_REACH)
{
Ogre::Vector3 dir = to - from;
dir.z = 0;
@ -69,7 +69,7 @@ namespace
// cast up-down ray and find height in world space of hit
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
if(abs(from.z - h) <= PATHFIND_Z_REACH)
if(std::abs(from.z - h) <= PATHFIND_Z_REACH)
return true;
}

View File

@ -1372,7 +1372,7 @@ void CharacterController::update(float duration)
//Force Jump Logic
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || std::abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
if(!inwater && !flying)
{
//Force Jump

View File

@ -29,7 +29,7 @@ Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 norma
);
}
void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition)
bool applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition)
{
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
if (!enchantmentName.empty())
@ -41,8 +41,10 @@ void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim,
MWMechanics::CastSpell cast(attacker, victim);
cast.mHitPosition = hitPosition;
cast.cast(object);
return true;
}
}
return false;
}
}
@ -216,15 +218,16 @@ namespace MWMechanics
damage *= gmst.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" effect of the weapon
applyEnchantment(attacker, victim, weapon, hitPosition);
bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition);
if (weapon != projectile)
applyEnchantment(attacker, victim, projectile, hitPosition);
appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition);
if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
// Arrows shot at enemies have a chance to turn up in their inventory
if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr())
// Non-enchanted arrows shot at enemies have a chance to turn up in their inventory
if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr()
&& !appliedEnchantment)
{
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f)

View File

@ -70,9 +70,9 @@ namespace MWMechanics
else
{
if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, failChance);
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false, failChance);
else
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, failChance);
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true, failChance);
}
}

View File

@ -572,8 +572,7 @@ namespace MWMechanics
float reaction = 0;
int rank = 0;
std::string npcFaction = "";
if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first;
std::string npcFaction = ptr.getClass().getPrimaryFaction(ptr);
Misc::StringUtils::toLower(npcFaction);
@ -1156,10 +1155,10 @@ namespace MWMechanics
// If committing a crime against a faction member, expell from the faction
if (!victim.isEmpty() && victim.getClass().isNpc())
{
std::string factionID;
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
std::string factionID = victim.getClass().getPrimaryFaction(victim);
const std::map<std::string, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();
if (playerRanks.find(Misc::StringUtils::lowerCase(factionID)) != playerRanks.end())
{
player.getClass().getNpcStats(player).expell(factionID);
}

View File

@ -91,12 +91,6 @@ void MWMechanics::NpcStats::lowerRank(const std::string &faction)
}
}
void MWMechanics::NpcStats::setFactionRank(const std::string &faction, int rank)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
mFactionRank[lower] = rank;
}
void MWMechanics::NpcStats::joinFaction(const std::string& faction)
{
const std::string lower = Misc::StringUtils::lowerCase(faction);
@ -127,14 +121,9 @@ void MWMechanics::NpcStats::clearExpelled(const std::string& factionID)
mExpelled.erase(Misc::StringUtils::lowerCase(factionID));
}
bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const
bool MWMechanics::NpcStats::isInFaction (const std::string& faction) const
{
for (std::map<std::string, int>::const_iterator iter (mFactionRank.begin()); iter!=mFactionRank.end();
++iter)
if (npcStats.mFactionRank.find (iter->first)!=npcStats.mFactionRank.end())
return true;
return false;
return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end());
}
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,

View File

@ -33,9 +33,7 @@ namespace MWMechanics
// ----- used by the player only, maybe should be moved at some point -------
int mBounty;
int mWerewolfKills;
/// NPCs other than the player can only have one faction. But for the sake of consistency
/// we use the same data structure for the PC and the NPCs.
/// \note the faction key must be in lowercase
/// Used for the player only; NPCs have maximum one faction defined in their NPC record
std::map<std::string, int> mFactionRank;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
@ -74,17 +72,13 @@ namespace MWMechanics
void lowerRank(const std::string& faction);
/// Join this faction, setting the initial rank to 0.
void joinFaction(const std::string& faction);
/// Warning: this function performs no check whether the rank exists,
/// and should be used in initial actor setup only.
void setFactionRank(const std::string& faction, int rank);
const std::set<std::string>& getExpelled() const { return mExpelled; }
bool getExpelled(const std::string& factionID) const;
void expell(const std::string& factionID);
void clearExpelled(const std::string& factionID);
bool isSameFaction (const NpcStats& npcStats) const;
///< Do *this and \a npcStats share a faction?
bool isInFaction (const std::string& faction) const;
float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1,
int level = -1, float extraFactor=1.f) const;

View File

@ -115,8 +115,8 @@ namespace MWMechanics
if(mDistSameSpot == -1)
mDistSameSpot = DIST_SAME_SPOT * (cls.getSpeed(actor) / 150);
bool samePosition = (abs(pos.pos[0] - mPrevX) < mDistSameSpot) &&
(abs(pos.pos[1] - mPrevY) < mDistSameSpot);
bool samePosition = (std::abs(pos.pos[0] - mPrevX) < mDistSameSpot) &&
(std::abs(pos.pos[1] - mPrevY) < mDistSameSpot);
// update position
mPrevX = pos.pos[0];
mPrevY = pos.pos[1];

View File

@ -66,108 +66,83 @@ namespace MWRender
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
loadingListener->setProgress(0);
const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19);
const Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13);
const Ogre::ColourValue groundColour(0.254, 0.19, 0.13);
const Ogre::ColourValue mountainColour(0.05, 0.05, 0.05);
const Ogre::ColourValue hillColour(0.16, 0.12, 0.08);
std::vector<Ogre::uchar> data (mWidth * mHeight * 3);
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
if (1)
for (int x = mMinX; x <= mMaxX; ++x)
{
std::vector<Ogre::uchar> data (mWidth * mHeight * 3);
for (int x = mMinX; x <= mMaxX; ++x)
for (int y = mMinY; y <= mMaxY; ++y)
{
for (int y = mMinY; y <= mMaxY; ++y)
ESM::Land* land = esmStore.get<ESM::Land>().search (x,y);
if (land)
{
ESM::Land* land = esmStore.get<ESM::Land>().search (x,y);
int mask = ESM::Land::DATA_WNAM;
if (!land->isDataLoaded(mask))
land->loadData(mask);
}
if (land)
for (int cellY=0; cellY<mCellSize; ++cellY)
{
for (int cellX=0; cellX<mCellSize; ++cellX)
{
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(mask))
land->loadData(mask);
}
int vertexX = float(cellX)/float(mCellSize) * 9;
int vertexY = float(cellY)/float(mCellSize) * 9;
for (int cellY=0; cellY<mCellSize; ++cellY)
{
for (int cellX=0; cellX<mCellSize; ++cellX)
int texelX = (x-mMinX) * mCellSize + cellX;
int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY);
unsigned char r,g,b;
float y = 0;
if (land && land->mDataTypes & ESM::Land::DATA_WNAM)
y = (land->mLandData->mWnam[vertexY * 9 + vertexX] << 4) / 2048.f;
else
y = (SCHAR_MIN << 4) / 2048.f;
if (y < 0)
{
int vertexX = float(cellX)/float(mCellSize) * ESM::Land::LAND_SIZE;
int vertexY = float(cellY)/float(mCellSize) * ESM::Land::LAND_SIZE;
int texelX = (x-mMinX) * mCellSize + cellX;
int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY);
unsigned char r,g,b;
if (land)
{
const float landHeight = land->mLandData->mHeights[vertexY * ESM::Land::LAND_SIZE + vertexX];
if (landHeight >= 0)
{
const float hillHeight = 2500.f;
if (landHeight >= hillHeight)
{
const float mountainHeight = 15000.f;
float factor = std::min(1.f, float(landHeight-hillHeight)/mountainHeight);
r = (hillColour.r * (1-factor) + mountainColour.r * factor) * 255;
g = (hillColour.g * (1-factor) + mountainColour.g * factor) * 255;
b = (hillColour.b * (1-factor) + mountainColour.b * factor) * 255;
}
else
{
float factor = std::min(1.f, float(landHeight)/hillHeight);
r = (groundColour.r * (1-factor) + hillColour.r * factor) * 255;
g = (groundColour.g * (1-factor) + hillColour.g * factor) * 255;
b = (groundColour.b * (1-factor) + hillColour.b * factor) * 255;
}
}
else
{
if (landHeight >= -100)
{
float factor = std::min(1.f, -1*landHeight/100.f);
r = (((waterShallowColour+groundColour)/2).r * (1-factor) + waterShallowColour.r * factor) * 255;
g = (((waterShallowColour+groundColour)/2).g * (1-factor) + waterShallowColour.g * factor) * 255;
b = (((waterShallowColour+groundColour)/2).b * (1-factor) + waterShallowColour.b * factor) * 255;
}
else
{
float factor = std::min(1.f, -1*(landHeight-100)/1000.f);
r = (waterShallowColour.r * (1-factor) + waterDeepColour.r * factor) * 255;
g = (waterShallowColour.g * (1-factor) + waterDeepColour.g * factor) * 255;
b = (waterShallowColour.b * (1-factor) + waterDeepColour.b * factor) * 255;
}
}
}
r = (14 * y + 38);
g = 20 * y + 56;
b = 18 * y + 51;
}
else if (y < 0.3f)
{
if (y < 0.1f)
y *= 8.f;
else
{
r = waterDeepColour.r * 255;
g = waterDeepColour.g * 255;
b = waterDeepColour.b * 255;
y -= 0.1;
y += 0.8;
}
data[texelY * mWidth * 3 + texelX * 3] = r;
data[texelY * mWidth * 3 + texelX * 3+1] = g;
data[texelY * mWidth * 3 + texelX * 3+2] = b;
r = 66 - 32 * y;
g = 48 - 23 * y;
b = 33 - 16 * y;
}
else
{
y -= 0.3f;
y *= 1.428f;
r = 34 - 29 * y;
g = 25 - 20 * y;
b = 17 - 12 * y;
}
data[texelY * mWidth * 3 + texelX * 3] = r;
data[texelY * mWidth * 3 + texelX * 3+1] = g;
data[texelY * mWidth * 3 + texelX * 3+2] = b;
}
}
loadingListener->increaseProgress();
if (land)
land->unloadData();
}
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC);
tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8);
}
else
tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png");
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC);
tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8);
tex->load();
@ -267,9 +242,9 @@ namespace MWRender
{
const ESM::GlobalMap::Bounds& bounds = map.mBounds;
if (bounds.mMaxX-bounds.mMinX <= 0)
if (bounds.mMaxX-bounds.mMinX < 0)
return;
if (bounds.mMaxY-bounds.mMinY <= 0)
if (bounds.mMaxY-bounds.mMinY < 0)
return;
if (bounds.mMinX > bounds.mMaxX

View File

@ -1020,7 +1020,10 @@ void NpcAnimation::setVampire(bool vampire)
return;
if ((mNpcType == Type_Vampire) != vampire)
{
rebuild();
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
MWBase::Environment::get().getWorld()->reattachPlayerCamera();
else
rebuild();
}
}

View File

@ -1036,10 +1036,10 @@ void RenderingManager::enableTerrain(bool enable)
if (!mTerrain)
{
if (Settings::Manager::getBool("distant land", "Terrain"))
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(true), RV_Terrain,
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
else
mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(false), RV_Terrain,
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
Settings::Manager::getBool("split", "Shadows"));

View File

@ -9,6 +9,22 @@
namespace MWRender
{
TerrainStorage::TerrainStorage(bool preload)
{
if (preload)
{
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();
for (; it != esmStore.get<ESM::Land>().end(); ++it)
{
ESM::Land* land = const_cast<ESM::Land*>(&*it); // TODO: fix store interface
land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX);
}
}
}
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
{
minX = 0, minY = 0, maxX = 0, maxY = 0;
@ -39,6 +55,12 @@ namespace MWRender
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);
if (!land)
return NULL;
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(flags))
land->loadData(flags);
return land;
}

View File

@ -14,6 +14,10 @@ namespace MWRender
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
public:
///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this
/// should be set to "true" in order to avoid race conditions.
TerrainStorage(bool preload);
/// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
};

View File

@ -196,7 +196,7 @@ namespace MWScript
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
runtime.push (ptr.getClass().getNpcStats (ptr).isSameFaction (player.getClass().getNpcStats (player)));
player.getClass().getNpcStats (player).isInFaction(ptr.getClass().getPrimaryFaction(ptr));
}
};

View File

@ -2,6 +2,7 @@
#include "globalscripts.hpp"
#include <cassert>
#include <iostream>
#include <components/misc/stringops.hpp>
#include <components/esm/globalscript.hpp>

View File

@ -313,17 +313,19 @@ namespace MWScript
std::string InterpreterContext::getNPCRank() const
{
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
const MWWorld::Ptr& ptr = getReferenceImp();
std::string faction = ptr.getClass().getPrimaryFaction(ptr);
if (faction.empty())
throw std::runtime_error("getNPCRank(): NPC is not in a faction");
const std::map<std::string, int>& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.begin();
int rank = ptr.getClass().getPrimaryFactionRank(ptr);
if (rank < 0 || rank > 9)
throw std::runtime_error("getNPCRank(): invalid rank");
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::ESMStore &store = world->getStore();
const ESM::Faction *faction = store.get<ESM::Faction>().find(it->first);
return faction->mRanks[it->second];
const ESM::Faction *fact = store.get<ESM::Faction>().find(faction);
return fact->mRanks[rank];
}
std::string InterpreterContext::getPCName() const
@ -352,13 +354,12 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
if (factionId.empty())
throw std::runtime_error("getPCRank(): NPC is not in a faction");
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
int rank = -1;
if (it != ranks.end())
rank = it->second;
@ -382,13 +383,12 @@ namespace MWScript
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
if (getReferenceImp().getClass().getNpcStats(getReferenceImp()).getFactionRanks().empty())
std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
if (factionId.empty())
throw std::runtime_error("getPCNextRank(): NPC is not in a faction");
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
int rank = -1;
if (it != ranks.end())
rank = it->second;

View File

@ -32,13 +32,12 @@ namespace
{
std::string getDialogueActorFaction(MWWorld::Ptr actor)
{
const MWMechanics::NpcStats &stats = actor.getClass().getNpcStats (actor);
if (stats.getFactionRanks().empty())
std::string factionId = actor.getClass().getPrimaryFaction(actor);
if (factionId.empty())
throw std::runtime_error (
"failed to determine dialogue actors faction (because actor is factionless)");
return stats.getFactionRanks().begin()->first;
return factionId;
}
}
@ -665,14 +664,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
@ -779,8 +771,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
@ -815,8 +806,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
@ -850,8 +840,7 @@ namespace MWScript
}
else
{
if (!ptr.getClass().getNpcStats (ptr).getFactionRanks().empty())
factionId = ptr.getClass().getNpcStats (ptr).getFactionRanks().begin()->first;
factionId = getDialogueActorFaction(ptr);
}
if (factionId.empty())
@ -941,14 +930,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
::Misc::StringUtils::toLower(factionID);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -980,14 +962,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="")
@ -1014,14 +989,7 @@ namespace MWScript
}
else
{
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
{
factionID = "";
}
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
factionID = ptr.getClass().getPrimaryFaction(ptr);
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="")
@ -1038,13 +1006,10 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = "";
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
std::string factionID = ptr.getClass().getPrimaryFaction(ptr);
if(factionID.empty())
return;
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// no-op when executed on the player
@ -1064,13 +1029,10 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
std::string factionID = "";
if(ptr.getClass().getNpcStats(ptr).getFactionRanks().empty())
std::string factionID = ptr.getClass().getPrimaryFaction(ptr);
if(factionID.empty())
return;
else
{
factionID = ptr.getClass().getNpcStats(ptr).getFactionRanks().begin()->first;
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// no-op when executed on the player

View File

@ -445,4 +445,13 @@ namespace MWWorld
{
throw std::runtime_error("class does not support fight rating");
}
std::string Class::getPrimaryFaction (const MWWorld::Ptr& ptr) const
{
return std::string();
}
int Class::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const
{
return -1;
}
}

View File

@ -342,6 +342,9 @@ namespace MWWorld
virtual std::string getSound(const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const;
virtual std::string getPrimaryFaction (const MWWorld::Ptr& ptr) const;
virtual int getPrimaryFactionRank (const MWWorld::Ptr& ptr) const;
};
}

View File

@ -1,7 +1,7 @@
#include "esmloader.hpp"
#include "esmstore.hpp"
#include "components/to_utf8/to_utf8.hpp"
#include <components/esm/esmreader.hpp>
namespace MWWorld
{

View File

@ -7,6 +7,8 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/esmreader.hpp>
namespace MWWorld
{

View File

@ -1,5 +1,7 @@
#include "localscripts.hpp"
#include <iostream>
#include "esmstore.hpp"
#include "cellstore.hpp"

View File

@ -5,6 +5,8 @@
#include <components/esm/records.hpp>
#include <components/misc/stringops.hpp>
namespace MWWorld
{
struct RecordCmp

View File

@ -231,6 +231,11 @@ namespace MWWorld
cell->getCell()->getGridY()
);
if (land) {
// Actually only VHGT is needed here, but we'll need the rest for rendering anyway.
// Load everything now to reduce IO overhead.
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(flags))
land->loadData(flags);
mPhysics->addHeightField (
land->mLandData->mHeights,
cell->getCell()->getGridX(),

View File

@ -1,6 +1,8 @@
#include "store.hpp"
#include "esmstore.hpp"
#include <components/esm/esmreader.hpp>
namespace MWWorld {
void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell)
@ -117,4 +119,9 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
}
}
void Store<ESM::LandTexture>::load(ESM::ESMReader &esm, const std::string &id)
{
load(esm, id, esm.getIndex());
}
}

View File

@ -5,6 +5,7 @@
#include <vector>
#include <map>
#include <stdexcept>
#include <sstream>
#include <components/esm/esmwriter.hpp>
@ -436,9 +437,7 @@ namespace MWWorld
ltexl[lt.mIndex] = lt;
}
void load(ESM::ESMReader &esm, const std::string &id) {
load(esm, id, esm.getIndex());
}
void load(ESM::ESMReader &esm, const std::string &id);
iterator begin(size_t plugin) const {
assert(plugin < mStatic.size());

View File

@ -2457,6 +2457,11 @@ namespace MWWorld
return mLevitationEnabled;
}
void World::reattachPlayerCamera()
{
mRendering->rebuildPtr(getPlayerPtr());
}
void World::setWerewolf(const MWWorld::Ptr& actor, bool werewolf)
{
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);

View File

@ -537,6 +537,7 @@ namespace MWWorld
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr);
virtual void reattachPlayerCamera();
/// \todo this does not belong here
virtual void frameStarted (float dt, bool paused);

View File

@ -58,7 +58,7 @@ add_component_dir (to_utf8
add_component_dir (esm
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
@ -113,7 +113,7 @@ add_component_dir (ogreinit
)
add_component_dir (widgets
box imagebutton tags list numericeditbox sharedstatebutton widgets
box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets
)
add_component_dir (fontloader

View File

@ -53,18 +53,6 @@ typedef NAME_T<32> NAME32;
typedef NAME_T<64> NAME64;
typedef NAME_T<256> NAME256;
#pragma pack(push)
#pragma pack(1)
// Data that is only present in save game files
struct SaveData
{
float pos[6]; // Player position and rotation
NAME64 cell; // Cell name
float unk2; // Unknown value - possibly game time?
NAME32 player; // Player name
};
#pragma pack(pop)
/* This struct defines a file 'context' which can be saved and later
restored by an ESMReader instance. It will save the position within
a file, and when restored will let you read from that position as

View File

@ -4,6 +4,8 @@
#include <fstream>
#include <stdexcept>
#include <components/to_utf8/to_utf8.hpp>
namespace ESM
{
ESMWriter::ESMWriter()

View File

@ -4,11 +4,14 @@
#include <iosfwd>
#include <list>
#include <components/to_utf8/to_utf8.hpp>
#include "esmcommon.hpp"
#include "loadtes3.hpp"
namespace ToUTF8
{
class Utf8Encoder;
}
namespace ESM {
class ESMWriter

View File

@ -12,7 +12,7 @@ namespace ESM
struct GlobalScript
{
std::string mId;
std::string mId; /// \note must be lowercase
Locals mLocals;
int mRunning;
std::string mTargetId; // for targeted scripts

View File

@ -32,7 +32,11 @@ struct DialInfo
struct DATAstruct
{
int mUnknown1;
int mDisposition;
union
{
int mDisposition; // Used for dialogue responses
int mJournalIndex; // Used for journal entries
};
signed char mRank; // Rank of NPC
signed char mGender; // See Gender enum
signed char mPCrank; // Player rank

View File

@ -72,7 +72,6 @@ Land::Land()
, mDataLoaded(false)
, mLandData(NULL)
, mPlugin(0)
, mHasData(false)
{
}
@ -97,8 +96,6 @@ void Land::load(ESMReader &esm)
// Store the file position
mContext = esm.getContext();
mHasData = false;
// Skip these here. Load the actual data when the cell is loaded.
if (esm.isNextSub("VNML"))
{
@ -126,10 +123,6 @@ void Land::load(ESMReader &esm)
mDataTypes |= DATA_VTEX;
}
// We need all three of VNML, VHGT and VTEX in order to use the
// landscape. (Though Morrowind seems to accept terrain without VTEX/VCLR entries)
mHasData = mDataTypes & (DATA_VNML|DATA_VHGT|DATA_WNAM);
mDataLoaded = 0;
mLandData = NULL;
}
@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const
esm.writeHNT("DATA", mFlags);
}
/// \todo remove memory allocation when only defaults needed
void Land::loadData(int flags)
{
// Try to load only available data
int actual = flags & mDataTypes;
flags = flags & mDataTypes;
// Return if all required data is loaded
if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) {
if ((mDataLoaded & flags) == flags) {
return;
}
// Create storage if nothing is loaded
@ -160,15 +152,13 @@ void Land::loadData(int flags)
}
mEsm->restoreContext(mContext);
memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals));
if (mEsm->isNextSub("VNML")) {
condLoad(actual, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
}
if (mEsm->isNextSub("VHGT")) {
static VHGT vhgt;
if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(vhgt))) {
if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
float rowOffset = vhgt.mHeightOffset;
for (int y = 0; y < LAND_SIZE; y++) {
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
@ -184,30 +174,18 @@ void Land::loadData(int flags)
mLandData->mUnk1 = vhgt.mUnk1;
mLandData->mUnk2 = vhgt.mUnk2;
}
} else if ((flags & DATA_VHGT) && (mDataLoaded & DATA_VHGT) == 0) {
for (int i = 0; i < LAND_NUM_VERTS; ++i) {
mLandData->mHeights[i] = -256.0f * HEIGHT_SCALE;
}
mDataLoaded |= DATA_VHGT;
}
if (mEsm->isNextSub("WNAM")) {
condLoad(actual, DATA_WNAM, mLandData->mWnam, 81);
}
if (mEsm->isNextSub("VCLR")) {
mLandData->mUsingColours = true;
condLoad(actual, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
} else {
mLandData->mUsingColours = false;
condLoad(flags, DATA_WNAM, mLandData->mWnam, 81);
}
if (mEsm->isNextSub("VCLR"))
condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
if (mEsm->isNextSub("VTEX")) {
static uint16_t vtex[LAND_NUM_TEXTURES];
if (condLoad(actual, DATA_VTEX, vtex, sizeof(vtex))) {
if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) {
LandData::transposeTextureData(vtex, mLandData->mTextures);
}
} else if ((flags & DATA_VTEX) && (mDataLoaded & DATA_VTEX) == 0) {
memset(mLandData->mTextures, 0, sizeof(mLandData->mTextures));
mDataLoaded |= DATA_VTEX;
}
}
@ -232,4 +210,9 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size)
return false;
}
bool Land::isDataLoaded(int flags) const
{
return (mDataLoaded & flags) == (flags & mDataTypes);
}
}

View File

@ -32,7 +32,6 @@ struct Land
ESMReader* mEsm;
ESM_Context mContext;
bool mHasData;
int mDataTypes;
int mDataLoaded;
@ -81,16 +80,12 @@ struct Land
VNML mNormals[LAND_NUM_VERTS * 3];
uint16_t mTextures[LAND_NUM_TEXTURES];
bool mUsingColours;
char mColours[3 * LAND_NUM_VERTS];
int mDataTypes;
// WNAM appears to contain the global map image for this cell. Probably a palette-based format,
// since there's only 1 byte for each pixel.
// Currently unused (global map is drawn on the fly in OpenMW, takes ~1/2 second at startup for Morrowind.esm).
// The problem with using the original data is that we would need to exactly replicate the TES CS's algorithm
// for drawing the global map in OpenCS, in order to get seamless edges when creating landmass mods.
uint8_t mWnam[81];
// low-LOD heightmap (used for rendering the global map)
signed char mWnam[81];
short mUnk1;
uint8_t mUnk2;
@ -116,10 +111,8 @@ struct Land
void unloadData();
/// Check if given data type is loaded
/// \todo reimplement this
bool isDataLoaded(int flags) {
return (mDataLoaded & flags) == flags;
}
/// @note We only check data types that *can* be loaded (present in mDataTypes)
bool isDataLoaded(int flags) const;
private:
Land(const Land& land);

View File

@ -144,4 +144,14 @@ void NPC::save(ESMWriter &esm) const
mHair.clear();
mHead.clear();
}
int NPC::getFactionRank() const
{
if (mFaction.empty())
return -1;
else if (mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
return mNpdt12.mRank;
else // NPC_DEFAULT
return mNpdt52.mRank;
}
}

View File

@ -110,6 +110,8 @@ struct NPC
NPDTstruct52 mNpdt52;
NPDTstruct12 mNpdt12; //for autocalculated characters
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
int mFlags;
bool mPersistent;

View File

@ -1,82 +0,0 @@
#ifndef OPENMW_ESM_NPCC_H
#define OPENMW_ESM_NPCC_H
#include <string>
// TODO: create implementation files to remove this
#include "esmreader.hpp"
namespace ESM {
class ESMReader;
class ESMWriter;
/*
* NPC change information (found in savegame files only). We can't
* read these yet.
*
* Some general observations about savegames:
*
* SCPT records do not define new scripts, but assign values to the
* variables of existing ones.
*
* VFXM, SPLM - no clue
*
* FMAP - MAPH and MAPD, global map image.
*
* JOUR - the entire journal in html
*
* QUES - seems to contain all the quests in the game, not just the
* ones you have done or begun.
*
* REGN - lists all regions in the game, even unvisited ones.
* notable differences to Regions in ESM files: mMapColor may be missing, and includes an unknown WNAM subrecord.
*
*
* The DIAL/INFO blocks contain changes to characters' dialog status.
*
* Dammit there's a lot of stuff in there! Should really have
* suspected as much. The strategy further is to completely ignore
* save files for the time being.
*
* Several records have a "change" variant, like NPCC, CNTC
* (contents), and CREC (creature.) These seem to alter specific
* instances of creatures, npcs, etc. I have not identified most of
* their subrecords yet.
*
* Several NPCC records have names that begin with "chargen ", I don't
* know if it means something special yet.
*
* The CNTC blocks seem to be instances of leveled lists. When a
* container is supposed to contain this leveled list of this type,
* but is referenced elsewhere in the file by an INDX, the CNTC with
* the corresponding leveled list identifier and INDX will determine
* the container contents instead.
*
* Some classes of objects seem to be altered, and these include an
* INDX, which is probably an index used by specific references other
* places within the save file. I guess this means 'use this class for
* these objects, not the general class.' All the indices I have
* encountered so far are zero, but they have been for different
* classes (different containers, really) so possibly we start from
* zero for each class. This looks like a mess, but is probably still
* easier than to duplicate everything. I think WRITING this format
* will be harder than reading it.
*/
struct LoadNPCC
{
static unsigned int sRecordId;
std::string mId;
void load(ESMReader &esm)
{
esm.skipRecord();
}
void save(ESMWriter &esm) const
{
}
};
}
#endif

View File

@ -34,7 +34,7 @@ namespace ESM
StatState<int> mWerewolfAttributes[8];
bool mIsWerewolf;
std::map<std::string, Faction> mFactions;
std::map<std::string, Faction> mFactions; // lower case IDs
int mDisposition;
Skill mSkills[27];
int mBounty;
@ -43,7 +43,7 @@ namespace ESM
int mProfit;
int mLevelProgress;
int mSkillIncrease[8];
std::vector<std::string> mUsedIds;
std::vector<std::string> mUsedIds; // lower case IDs
float mTimeToStartDrowning;
int mCrimeId;

View File

@ -12,7 +12,7 @@ namespace ESM
struct QuestState
{
std::string mTopic;
std::string mTopic; // lower case id
int mState;
unsigned char mFinished;
@ -21,4 +21,4 @@ namespace ESM
};
}
#endif
#endif

View File

@ -32,7 +32,6 @@
#include "loadmgef.hpp"
#include "loadmisc.hpp"
#include "loadnpc.hpp"
#include "loadnpcc.hpp"
#include "loadpgrd.hpp"
#include "loadrace.hpp"
#include "loadregn.hpp"

View File

@ -31,7 +31,7 @@ namespace ESMTerrain
int cellY = origin.y;
const ESM::Land* land = getLand(cellX, cellY);
if (!land)
if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT))
return false;
min = std::numeric_limits<float>::max();
@ -73,7 +73,7 @@ namespace ESMTerrain
row += ESM::Land::LAND_SIZE-1;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mHasData)
if (land && land->mDataTypes&ESM::Land::DATA_VNML)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
@ -108,7 +108,7 @@ namespace ESMTerrain
row = 0;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mLandData->mUsingColours)
if (land && land->mDataTypes&ESM::Land::DATA_VCLR)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
@ -157,9 +157,8 @@ namespace ESMTerrain
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
{
ESM::Land* land = getLand(cellX, cellY);
if (land && !land->mHasData)
if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT))
land = NULL;
bool hasColors = land && land->mLandData->mUsingColours;
int rowStart = 0;
int colStart = 0;
@ -183,7 +182,7 @@ namespace ESMTerrain
else
positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
if (land)
if (land && land->mDataTypes&ESM::Land::DATA_VNML)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
@ -207,7 +206,7 @@ namespace ESMTerrain
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
if (hasColors)
if (land && land->mDataTypes&ESM::Land::DATA_VCLR)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
@ -263,7 +262,7 @@ namespace ESMTerrain
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
ESM::Land* land = getLand(cellX, cellY);
if (land)
if (land && (land->mDataTypes&ESM::Land::DATA_VTEX))
{
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
if (tex == 0)

View File

@ -7,6 +7,7 @@
#include "box.hpp"
#include "imagebutton.hpp"
#include "sharedstatebutton.hpp"
#include "windowcaption.hpp"
namespace Gui
{
@ -22,6 +23,7 @@ namespace Gui
MyGUI::FactoryManager::getInstance().registerFactory<Gui::ImageButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::NumericEditBox>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::SharedStateButton>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<Gui::WindowCaption>("Widget");
}
}

View File

@ -0,0 +1,58 @@
#include "windowcaption.hpp"
#include <stdexcept>
namespace Gui
{
WindowCaption::WindowCaption()
: mLeft(NULL)
, mRight(NULL)
{
}
void WindowCaption::initialiseOverride()
{
Base::initialiseOverride();
assignWidget(mLeft, "Left");
assignWidget(mRight, "Right");
assignWidget(mClient, "Client");
if (!mClient)
throw std::runtime_error("WindowCaption needs an EditBox Client widget in its skin");
}
void WindowCaption::setCaption(const MyGUI::UString &_value)
{
EditBox::setCaption(_value);
align();
}
void WindowCaption::setSize(const MyGUI::IntSize& _value)
{
Base::setSize(_value);
align();
}
void WindowCaption::setCoord(const MyGUI::IntCoord& _value)
{
Base::setCoord(_value);
align();
}
void WindowCaption::align()
{
MyGUI::IntSize textSize = getTextSize();
MyGUI::Widget* caption = mClient;
caption->setSize(textSize.width + 24, caption->getHeight());
int barwidth = (getWidth()-caption->getWidth())/2;
caption->setPosition(barwidth, caption->getTop());
if (mLeft)
mLeft->setCoord(0, mLeft->getTop(), barwidth, mLeft->getHeight());
if (mRight)
mRight->setCoord(barwidth + caption->getWidth(), mRight->getTop(), barwidth, mRight->getHeight());
}
}

View File

@ -0,0 +1,32 @@
#ifndef OPENMW_WIDGETS_WINDOWCAPTION_H
#define OPENMW_WIDGETS_WINDOWCAPTION_H
#include <MyGUI_EditBox.h>
namespace Gui
{
/// Window caption that automatically adjusts "Left" and "Right" widgets in its skin
/// based on the text size of the caption in the middle
class WindowCaption : public MyGUI::EditBox
{
MYGUI_RTTI_DERIVED(WindowCaption)
public:
WindowCaption();
virtual void setCaption(const MyGUI::UString &_value);
virtual void initialiseOverride();
virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _value);
private:
MyGUI::Widget* mLeft;
MyGUI::Widget* mRight;
void align();
};
}
#endif

View File

@ -346,11 +346,11 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts)
* buffer. We use this to store the global_pts in
* a frame at the time it is allocated.
*/
static uint64_t global_video_pkt_pts = static_cast<uint64_t>(AV_NOPTS_VALUE);
static int64_t global_video_pkt_pts = AV_NOPTS_VALUE;
static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic)
{
int ret = avcodec_default_get_buffer(c, pic);
uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t));
int64_t *pts = (int64_t*)av_malloc(sizeof(int64_t));
*pts = global_video_pkt_pts;
pic->opaque = pts;
return ret;
@ -397,10 +397,10 @@ void VideoState::video_thread_loop(VideoState *self)
throw std::runtime_error("Error decoding video frame");
double pts = 0;
if((uint64_t)packet->dts != AV_NOPTS_VALUE)
if(packet->dts != AV_NOPTS_VALUE)
pts = packet->dts;
else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE)
pts = *(uint64_t*)pFrame->opaque;
else if(pFrame->opaque && *(int64_t*)pFrame->opaque != AV_NOPTS_VALUE)
pts = *(int64_t*)pFrame->opaque;
pts *= av_q2d((*self->video_st)->time_base);
av_free_packet(packet);

View File

@ -30,7 +30,8 @@ namespace SFO
mWantMouseVisible(false),
mAllowGrab(grab),
mWarpX(0),
mWarpY(0)
mWarpY(0),
mFirstMouseMove(true)
{
_setupOISKeys();
}
@ -316,6 +317,13 @@ namespace SFO
pack_evt.y = mMouseY = evt.motion.y;
pack_evt.xrel = evt.motion.xrel;
pack_evt.yrel = evt.motion.yrel;
if (mFirstMouseMove)
{
// first event should be treated as non-relative, since there's no point of reference
// SDL then (incorrectly) uses (0,0) as point of reference, on Linux at least...
pack_evt.xrel = pack_evt.yrel = 0;
mFirstMouseMove = false;
}
}
else if(evt.type == SDL_MOUSEWHEEL)
{

View File

@ -71,6 +71,8 @@ namespace SFO
bool mGrabPointer;
bool mMouseRelative;
bool mFirstMouseMove;
Sint32 mMouseZ;
Sint32 mMouseX;
Sint32 mMouseY;

View File

@ -6,8 +6,8 @@
<Widget type="Widget" skin="MW_Box" position="8 8 381 381" align="Stretch" name="Client"/>
<Widget type="Widget" position="13 13 357 371" align="Left Top Stretch">
<Widget type="BookPage" skin="MW_BookPage" position="0 0 357 371" name="History" align="Left Top Stretch">
<Widget type="Widget" position="15 15 364 370" align="Left Top Stretch">
<Widget type="BookPage" skin="MW_BookPage" position="0 0 364 370" name="History" align="Left Top Stretch">
</Widget>
</Widget>

View File

@ -6,14 +6,14 @@
<Resource type="ResourceSkin" name="HUD_Box" size="40 40">
<!-- The interior of the box -->
<Child type="Widget" skin="BlackBG" offset="0 0 40 40" align="Stretch" name="Client"/>
<!-- Borders -->
<Child type="Widget" skin="MW_Box" offset="0 0 40 40" align="Left Stretch" name="Client"/>
<!-- The interior of the box -->
<Child type="Widget" skin="BlackBG" offset="2 2 36 36" align="Stretch" name="Client"/>
</Resource>
<Resource type="ResourceSkin" name="HUD_Box_Transparent" size="40 40">
@ -22,14 +22,14 @@
<Resource type="ResourceSkin" name="HUD_Box_NoTransp" size="40 40">
<!-- The interior of the box -->
<Child type="Widget" skin="DialogBG" offset="0 0 40 40" align="Stretch" name="Client"/>
<!-- Borders -->
<Child type="Widget" skin="MW_Box" offset="0 0 40 40" align="Left Stretch" name="Client"/>
<!-- The interior of the box -->
<Child type="Widget" skin="DialogBG" offset="2 2 36 36" align="Stretch" name="Client"/>
</Resource>
</MyGUI>

View File

@ -395,17 +395,9 @@
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Center"/>
<Child type="Widget" skin="DialogBG" offset="0 0 88 20" align="Stretch"/>
<Child type="TextBox" skin="SandText" offset="2 0 84 20" align="Stretch" name="Client"/>
<!-- Add the borders of the surrounding blocks -->
<Child type="Widget" skin="HB_LEFT" offset="86 2 2 16" align="Top Right"/>
<Child type="Widget" skin="HB_RIGHT" offset="0 2 2 16" align="Top Left"/>
<Child type="Widget" skin="HB_TR" offset="0 0 2 2" align="Top Left"/>
<Child type="Widget" skin="HB_TL" offset="86 0 2 2" align="Top Right"/>
<Child type="Widget" skin="HB_BR" offset="0 18 2 2" align="Bottom Left"/>
<Child type="Widget" skin="HB_BL" offset="86 18 2 2" align="Bottom Right"/>
<Child type="Widget" skin="HB_ALL" offset="0 0 30 20" align="Default" name="Left"/>
<Child type="TextBox" skin="SandText" offset="30 0 28 20" align="Left VStretch" name="Client"/>
<Child type="Widget" skin="HB_ALL" offset="0 0 30 20" align="Right" name="Right"/>
</Resource>
<!-- ----------------------------------------------------
@ -420,7 +412,8 @@
<Property key="Snap" value="true"/>
<Property key="MinSize" value="64 64"/>
<Child type="Widget" skin="BlackBG" offset="8 28 240 220" align="Stretch" name="Client"/>
<Child type="Widget" skin="BlackBG" offset="0 0 256 256" align="Stretch"/>
<Child type="Widget" skin="" offset="8 28 240 220" align="Stretch" name="Client"/>
<!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action">
@ -538,12 +531,7 @@
<Property key="Scale" value="0 0 1 1"/>
</Child>
<!-- Caption -->
<Child type="Widget" skin="HB_ALL" offset="4 4 248 20" align="Top HStretch">
<Property key="Scale" value="1 1 0 0"/>
</Child>
<Child type="EditBox" skin="MW_Caption" offset="80 4 88 20" align="HCenter Top" name="Caption">
<Child type="WindowCaption" skin="MW_Caption" offset="4 4 248 20" align="HStretch Top" name="Caption">
</Child>
<!-- This invisible button makes it possible to move the
@ -559,7 +547,8 @@
<Property key="Snap" value="true"/>
<Property key="MinSize" value="64 64"/>
<Child type="Widget" skin="BlackBG" offset="8 28 240 220" align="Stretch" name="Client"/>
<Child type="Widget" skin="BlackBG" offset=" 0 0 256 256" align="Stretch"/>
<Child type="Widget" skin="" offset="8 28 240 220" align="Stretch" name="Client"/>
<!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action">
@ -695,7 +684,8 @@
<Property key="Snap" value="true"/>
<Property key="MinSize" value="64 64"/>
<Child type="Widget" skin="BlackBG" offset="8 28 240 220" align="Stretch" name="Client"/>
<Child type="Widget" skin="BlackBG" offset=" 0 0 256 256" align="Stretch"/>
<Child type="Widget" skin="" offset="8 28 240 220" align="Stretch" name="Client"/>
<!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action">
@ -814,12 +804,7 @@
</Child>
<!-- Caption -->
<Child type="Widget" skin="HB_ALL" offset="4 4 248 20" align="Top HStretch">
<Property key="Scale" value="1 1 0 0"/>
</Child>
<Child type="EditBox" skin="MW_Caption" offset="80 4 88 20" align="HCenter Top" name="Caption">
<Child type="WindowCaption" skin="MW_Caption" offset="4 4 228 20" align="HStretch Top" name="Caption">
</Child>
<!-- This invisible button makes it possible to move the

View File

@ -45,6 +45,8 @@ subtitles = false
hit fader = true
werewolf overlay = true
stretch menu background = false
[General]
# Camera field of view
field of view = 55

View File

@ -46,27 +46,6 @@ namespace GUI
mMainWidget->setCoord(x,y,w,h);
}
void Layout::adjustWindowCaption()
{
MyGUI::TextBox* box = mMainWidget->castType<MyGUI::Window>(mMainWidget)->getCaptionWidget();
box->setSize(box->getTextSize().width + 24, box->getSize().height);
// in order to trigger alignment updates, we need to update the parent
// mygui doesn't provide a proper way of doing this, so we are just changing size
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height+1
));
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height-1
));
}
void Layout::setVisible(bool b)
{
mMainWidget->setVisible(b);
@ -82,7 +61,6 @@ namespace GUI
void Layout::setTitle(const std::string& title)
{
static_cast<MyGUI::Window*>(mMainWidget)->setCaptionWithReplacing(title);
adjustWindowCaption();
}
MyGUI::Widget* Layout::getWidget(const std::string &_name)

View File

@ -45,10 +45,6 @@ namespace GUI
public:
void setCoord(int x, int y, int w, int h);
// adjust the size of the window caption so that all text is visible
// NOTE: this assumes that mMainWidget is of type Window.
void adjustWindowCaption();
virtual void setVisible(bool b);
void setText(const std::string& name, const std::string& caption);