Merge remote-tracking branch 'scrawl/master'

openmw-35
Marc Zinnschlag 10 years ago
commit 1150b41c18

@ -1,9 +1,7 @@
OpenMW OpenMW
====== ======
[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) [![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)
[![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 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. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.

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

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

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

@ -3,6 +3,7 @@
#include <stdexcept> #include <stdexcept>
#include <OgreImage.h> #include <OgreImage.h>
#include <OgreColourValue.h>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp> #include <components/esm/containerstate.hpp>
@ -69,7 +70,65 @@ namespace ESSImport
esm.getSubHeader(); esm.getSubHeader();
data.resize(esm.getSubSize()); data.resize(esm.getSubSize());
esm.getExact(&data[0], data.size()); 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) void ConvertCell::read(ESM::ESMReader &esm)
@ -103,6 +162,10 @@ namespace ESSImport
// (probably offset of that specific fog texture?) // (probably offset of that specific fog texture?)
while (esm.isNextSub("NAM8")) 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(); esm.getSubHeader();
if (esm.getSubSize() == 36) if (esm.getSubSize() == 36)
@ -313,10 +376,6 @@ namespace ESSImport
it->save(esm); it->save(esm);
esm.endRecord(ESM::REC_MARK); esm.endRecord(ESM::REC_MARK);
} }
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
} }
} }

@ -1,6 +1,8 @@
#ifndef OPENMW_ESSIMPORT_CONVERTER_H #ifndef OPENMW_ESSIMPORT_CONVERTER_H
#define OPENMW_ESSIMPORT_CONVERTER_H #define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
@ -13,6 +15,9 @@
#include <components/esm/dialoguestate.hpp> #include <components/esm/dialoguestate.hpp>
#include <components/esm/custommarkerstate.hpp> #include <components/esm/custommarkerstate.hpp>
#include <components/esm/loadcrea.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 "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
@ -29,6 +34,7 @@
#include "convertacdt.hpp" #include "convertacdt.hpp"
#include "convertnpcc.hpp" #include "convertnpcc.hpp"
#include "convertscpt.hpp"
namespace ESSImport namespace ESSImport
{ {
@ -206,7 +212,7 @@ public:
else else
{ {
int index = npcc.mNPDT.mIndex; 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.mExpelled = (it->mFlags & 0x2) != 0;
faction.mRank = it->mRank; faction.mRank = it->mRank;
faction.mReputation = it->mReputation; 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) for (int i=0; i<8; ++i)
mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
@ -308,6 +314,10 @@ class ConvertFMAP : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm); virtual void read(ESM::ESMReader &esm);
virtual void write(ESM::ESMWriter &esm);
private:
Ogre::Image mGlobalMapImage;
}; };
class ConvertCell : public Converter class ConvertCell : public Converter
@ -428,7 +438,24 @@ public:
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
DIAL dial; DIAL dial;
dial.load(esm); 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 class ConvertQUES : public Converter
@ -455,11 +482,69 @@ public:
class ConvertGAME : public Converter class ConvertGAME : public Converter
{ {
public: 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) virtual void read(ESM::ESMReader &esm)
{ {
GAME game; mGame.load(esm);
game.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 /// Running global script
@ -470,7 +555,21 @@ public:
{ {
SCPT script; SCPT script;
script.load(esm); 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;
}; };
} }

@ -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);
}
}

@ -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

@ -60,10 +60,6 @@ namespace ESSImport
if (esm.isNextSub("PWPS")) if (esm.isNextSub("PWPS"))
esm.skipHSub(); 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")) if (esm.isNextSub("WNAM"))
{ {
std::string id = esm.getHString(); std::string id = esm.getHString();
@ -77,6 +73,20 @@ namespace ESSImport
esm.skipHSub(); // 4 byte, 0 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 // FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int)); esm.getHExact(mSkills, 27*2*sizeof(int));

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

@ -7,7 +7,23 @@ namespace ESSImport
void GAME::load(ESM::ESMReader &esm) 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);
} }
} }

@ -20,7 +20,7 @@ namespace ESSImport
int mCurrentWeather, mNextWeather; int mCurrentWeather, mNextWeather;
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition 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; GMDT mGMDT;

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

@ -14,7 +14,6 @@ namespace ESSImport
{ {
// A running global script // A running global script
// TODO: test how targeted scripts are saved
struct SCPT struct SCPT
{ {
ESM::Script::SCHD mSCHD; ESM::Script::SCHD mSCHD;
@ -22,7 +21,8 @@ namespace ESSImport
// values of local variables // values of local variables
SCRI mSCRI; 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); void load(ESM::ESMReader& esm);
}; };

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

@ -296,25 +296,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); 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 // creature stats
int gold=0; int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) 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; 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>() static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt(); .find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt(); .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)); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
} }
@ -1371,4 +1352,16 @@ namespace MWClass
{ {
return true; 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();
}
} }

@ -189,6 +189,9 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (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;
}; };
} }

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

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

@ -259,7 +259,12 @@ namespace MWDialogue
record.load (reader); record.load (reader);
if (isThere (record.mTopic)) 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);
}
} }
} }
} }

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

@ -5,14 +5,14 @@
namespace MWGui 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) if (mChild)
{ {
MyGUI::Gui::getInstance().destroyWidget(mChild); MyGUI::Gui::getInstance().destroyWidget(mChild);
mChild = NULL; mChild = NULL;
} }
if (correct) if (!stretch)
{ {
setImageTexture("black.png"); setImageTexture("black.png");

@ -18,9 +18,9 @@ namespace MWGui
/** /**
* @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions * @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 setSize (const MyGUI::IntSize &_value);
virtual void setCoord (const MyGUI::IntCoord &_value); virtual void setCoord (const MyGUI::IntCoord &_value);

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

@ -154,8 +154,6 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); 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)); setTitle(container.getClass().getName(container));
} }

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

@ -37,8 +37,6 @@ namespace MWGui
virtual void setSize(const MyGUI::IntSize& _value); virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _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 onSelectedItem (MyGUI::Widget* sender);
void onSelectedBackground (MyGUI::Widget* sender); void onSelectedBackground (MyGUI::Widget* sender);

@ -149,7 +149,9 @@ namespace MWGui
Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); 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 // 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 else
std::cerr << "No loading screens found!" << std::endl; std::cerr << "No loading screens found!" << std::endl;

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

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

@ -45,8 +45,6 @@ namespace MWGui
virtual void setSize(const MyGUI::IntSize& _value); virtual void setSize(const MyGUI::IntSize& _value);
virtual void setCoord(const MyGUI::IntCoord& _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: private:
MyGUI::ScrollView* mScrollView; MyGUI::ScrollView* mScrollView;

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

@ -133,8 +133,6 @@ namespace MWGui
updateLabels(); 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)); setTitle(actor.getClass().getName(actor));
onFilterChanged(mFilterAll); onFilterChanged(mFilterAll);

@ -2,6 +2,8 @@
#include <extern/ogre-ffmpeg-videoplayer/videoplayer.hpp> #include <extern/ogre-ffmpeg-videoplayer/videoplayer.hpp>
#include <MyGUI_RenderManager.h>
#include "../mwsound/movieaudiofactory.hpp" #include "../mwsound/movieaudiofactory.hpp"
namespace MWGui namespace MWGui
@ -46,4 +48,24 @@ bool VideoWidget::hasAudioStream()
return mPlayer->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);
}
} }

@ -35,6 +35,12 @@ namespace MWGui
/// Stop video and free resources (done automatically on destruction) /// Stop video and free resources (done automatically on destruction)
void stop(); 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: private:
std::auto_ptr<Video::VideoPlayer> mPlayer; std::auto_ptr<Video::VideoPlayer> mPlayer;
}; };

@ -1712,18 +1712,9 @@ namespace MWGui
void WindowManager::sizeVideo(int screenWidth, int screenHeight) void WindowManager::sizeVideo(int screenWidth, int screenHeight)
{ {
// Use black bars to correct aspect ratio // Use black bars to correct aspect ratio
bool stretch = Settings::Manager::getBool("stretch menu background", "GUI");
mVideoBackground->setSize(screenWidth, screenHeight); mVideoBackground->setSize(screenWidth, screenHeight);
mVideoWidget->autoResize(stretch);
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);
}
} }
WindowModal* WindowManager::getCurrentModal() const WindowModal* WindowManager::getCurrentModal() const

@ -58,7 +58,7 @@ namespace
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH // magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offsetXY) 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; Ogre::Vector3 dir = to - from;
dir.z = 0; dir.z = 0;
@ -69,7 +69,7 @@ namespace
// cast up-down ray and find height in world space of hit // 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); 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; return true;
} }

@ -1372,7 +1372,7 @@ void CharacterController::update(float duration)
//Force Jump Logic //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) if(!inwater && !flying)
{ {
//Force Jump //Force Jump

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

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

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

@ -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) void MWMechanics::NpcStats::joinFaction(const std::string& faction)
{ {
const std::string lower = Misc::StringUtils::lowerCase(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)); 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(); return (mFactionRank.find(Misc::StringUtils::lowerCase(faction)) != mFactionRank.end());
++iter)
if (npcStats.mFactionRank.find (iter->first)!=npcStats.mFactionRank.end())
return true;
return false;
} }
float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType, float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType,

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

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

@ -66,108 +66,83 @@ namespace MWRender
loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1));
loadingListener->setProgress(0); loadingListener->setProgress(0);
const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); std::vector<Ogre::uchar> data (mWidth * mHeight * 3);
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);
//if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png"))
if (1)
{
std::vector<Ogre::uchar> data (mWidth * mHeight * 3);
for (int x = mMinX; x <= mMaxX; ++x) 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);
{
ESM::Land* land = esmStore.get<ESM::Land>().search (x,y);
if (land) if (land)
{ {
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; int mask = ESM::Land::DATA_WNAM;
if (!land->isDataLoaded(mask)) if (!land->isDataLoaded(mask))
land->loadData(mask); land->loadData(mask);
} }
for (int cellY=0; cellY<mCellSize; ++cellY) for (int cellY=0; cellY<mCellSize; ++cellY)
{
for (int cellX=0; cellX<mCellSize; ++cellX)
{ {
for (int cellX=0; cellX<mCellSize; ++cellX) int vertexX = float(cellX)/float(mCellSize) * 9;
{ int vertexY = float(cellY)/float(mCellSize) * 9;
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 texelX = (x-mMinX) * mCellSize + cellX;
int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY); int texelY = (mHeight-1) - ((y-mMinY) * mCellSize + cellY);
unsigned char r,g,b; 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;
}
}
} 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)
{
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 else
{ {
r = waterDeepColour.r * 255; y -= 0.1;
g = waterDeepColour.g * 255; y += 0.8;
b = waterDeepColour.b * 255;
} }
r = 66 - 32 * y;
data[texelY * mWidth * 3 + texelX * 3] = r; g = 48 - 23 * y;
data[texelY * mWidth * 3 + texelX * 3+1] = g; b = 33 - 16 * y;
data[texelY * mWidth * 3 + texelX * 3+2] = b;
} }
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())); Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 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); Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_STATIC);
tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8); tex->loadRawData(stream, mWidth, mHeight, Ogre::PF_B8G8R8);
}
else
tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png");
tex->load(); tex->load();
@ -267,9 +242,9 @@ namespace MWRender
{ {
const ESM::GlobalMap::Bounds& bounds = map.mBounds; const ESM::GlobalMap::Bounds& bounds = map.mBounds;
if (bounds.mMaxX-bounds.mMinX <= 0) if (bounds.mMaxX-bounds.mMinX < 0)
return; return;
if (bounds.mMaxY-bounds.mMinY <= 0) if (bounds.mMaxY-bounds.mMinY < 0)
return; return;
if (bounds.mMinX > bounds.mMaxX if (bounds.mMinX > bounds.mMaxX

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

@ -1036,10 +1036,10 @@ void RenderingManager::enableTerrain(bool enable)
if (!mTerrain) if (!mTerrain)
{ {
if (Settings::Manager::getBool("distant land", "Terrain")) 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); Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
else 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); Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
Settings::Manager::getBool("split", "Shadows")); Settings::Manager::getBool("split", "Shadows"));

@ -9,6 +9,22 @@
namespace MWRender 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) void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
{ {
minX = 0, minY = 0, maxX = 0, maxY = 0; minX = 0, minY = 0, maxX = 0, maxY = 0;
@ -39,6 +55,12 @@ namespace MWRender
const MWWorld::ESMStore &esmStore = const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY); 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; return land;
} }

@ -14,6 +14,10 @@ namespace MWRender
virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
public: 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 /// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
}; };

@ -196,7 +196,7 @@ namespace MWScript
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); 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));
} }
}; };

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

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

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

@ -445,4 +445,13 @@ namespace MWWorld
{ {
throw std::runtime_error("class does not support fight rating"); 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;
}
} }

@ -342,6 +342,9 @@ namespace MWWorld
virtual std::string getSound(const MWWorld::Ptr& ptr) const; virtual std::string getSound(const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (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;
}; };
} }

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

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

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

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

@ -231,6 +231,11 @@ namespace MWWorld
cell->getCell()->getGridY() cell->getCell()->getGridY()
); );
if (land) { 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 ( mPhysics->addHeightField (
land->mLandData->mHeights, land->mLandData->mHeights,
cell->getCell()->getGridX(), cell->getCell()->getGridX(),

@ -1,6 +1,8 @@
#include "store.hpp" #include "store.hpp"
#include "esmstore.hpp" #include "esmstore.hpp"
#include <components/esm/esmreader.hpp>
namespace MWWorld { namespace MWWorld {
void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) 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());
}
} }

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

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

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

@ -58,7 +58,7 @@ add_component_dir (to_utf8
add_component_dir (esm add_component_dir (esm
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst 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 loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter 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 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 add_component_dir (widgets
box imagebutton tags list numericeditbox sharedstatebutton widgets box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets
) )
add_component_dir (fontloader add_component_dir (fontloader

@ -53,18 +53,6 @@ typedef NAME_T<32> NAME32;
typedef NAME_T<64> NAME64; typedef NAME_T<64> NAME64;
typedef NAME_T<256> NAME256; 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 /* This struct defines a file 'context' which can be saved and later
restored by an ESMReader instance. It will save the position within restored by an ESMReader instance. It will save the position within
a file, and when restored will let you read from that position as a file, and when restored will let you read from that position as

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

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

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

@ -32,7 +32,11 @@ struct DialInfo
struct DATAstruct struct DATAstruct
{ {
int mUnknown1; 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 mRank; // Rank of NPC
signed char mGender; // See Gender enum signed char mGender; // See Gender enum
signed char mPCrank; // Player rank signed char mPCrank; // Player rank

@ -72,7 +72,6 @@ Land::Land()
, mDataLoaded(false) , mDataLoaded(false)
, mLandData(NULL) , mLandData(NULL)
, mPlugin(0) , mPlugin(0)
, mHasData(false)
{ {
} }
@ -97,8 +96,6 @@ void Land::load(ESMReader &esm)
// Store the file position // Store the file position
mContext = esm.getContext(); mContext = esm.getContext();
mHasData = false;
// Skip these here. Load the actual data when the cell is loaded. // Skip these here. Load the actual data when the cell is loaded.
if (esm.isNextSub("VNML")) if (esm.isNextSub("VNML"))
{ {
@ -126,10 +123,6 @@ void Land::load(ESMReader &esm)
mDataTypes |= DATA_VTEX; 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; mDataLoaded = 0;
mLandData = NULL; mLandData = NULL;
} }
@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const
esm.writeHNT("DATA", mFlags); esm.writeHNT("DATA", mFlags);
} }
/// \todo remove memory allocation when only defaults needed
void Land::loadData(int flags) void Land::loadData(int flags)
{ {
// Try to load only available data // Try to load only available data
int actual = flags & mDataTypes; flags = flags & mDataTypes;
// Return if all required data is loaded // Return if all required data is loaded
if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) { if ((mDataLoaded & flags) == flags) {
return; return;
} }
// Create storage if nothing is loaded // Create storage if nothing is loaded
@ -160,15 +152,13 @@ void Land::loadData(int flags)
} }
mEsm->restoreContext(mContext); mEsm->restoreContext(mContext);
memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals));
if (mEsm->isNextSub("VNML")) { 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")) { if (mEsm->isNextSub("VHGT")) {
static VHGT vhgt; static VHGT vhgt;
if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(vhgt))) { if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
float rowOffset = vhgt.mHeightOffset; float rowOffset = vhgt.mHeightOffset;
for (int y = 0; y < LAND_SIZE; y++) { for (int y = 0; y < LAND_SIZE; y++) {
rowOffset += vhgt.mHeightData[y * LAND_SIZE]; rowOffset += vhgt.mHeightData[y * LAND_SIZE];
@ -184,30 +174,18 @@ void Land::loadData(int flags)
mLandData->mUnk1 = vhgt.mUnk1; mLandData->mUnk1 = vhgt.mUnk1;
mLandData->mUnk2 = vhgt.mUnk2; 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")) { if (mEsm->isNextSub("WNAM")) {
condLoad(actual, DATA_WNAM, mLandData->mWnam, 81); condLoad(flags, 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;
} }
if (mEsm->isNextSub("VCLR"))
condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
if (mEsm->isNextSub("VTEX")) { if (mEsm->isNextSub("VTEX")) {
static uint16_t vtex[LAND_NUM_TEXTURES]; 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); 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; return false;
} }
bool Land::isDataLoaded(int flags) const
{
return (mDataLoaded & flags) == (flags & mDataTypes);
}
} }

@ -32,7 +32,6 @@ struct Land
ESMReader* mEsm; ESMReader* mEsm;
ESM_Context mContext; ESM_Context mContext;
bool mHasData;
int mDataTypes; int mDataTypes;
int mDataLoaded; int mDataLoaded;
@ -81,16 +80,12 @@ struct Land
VNML mNormals[LAND_NUM_VERTS * 3]; VNML mNormals[LAND_NUM_VERTS * 3];
uint16_t mTextures[LAND_NUM_TEXTURES]; uint16_t mTextures[LAND_NUM_TEXTURES];
bool mUsingColours;
char mColours[3 * LAND_NUM_VERTS]; char mColours[3 * LAND_NUM_VERTS];
int mDataTypes; int mDataTypes;
// WNAM appears to contain the global map image for this cell. Probably a palette-based format, // low-LOD heightmap (used for rendering the global map)
// since there's only 1 byte for each pixel. signed char mWnam[81];
// 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];
short mUnk1; short mUnk1;
uint8_t mUnk2; uint8_t mUnk2;
@ -116,10 +111,8 @@ struct Land
void unloadData(); void unloadData();
/// Check if given data type is loaded /// Check if given data type is loaded
/// \todo reimplement this /// @note We only check data types that *can* be loaded (present in mDataTypes)
bool isDataLoaded(int flags) { bool isDataLoaded(int flags) const;
return (mDataLoaded & flags) == flags;
}
private: private:
Land(const Land& land); Land(const Land& land);

@ -144,4 +144,14 @@ void NPC::save(ESMWriter &esm) const
mHair.clear(); mHair.clear();
mHead.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;
}
} }

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

@ -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

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

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

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

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

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

@ -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());
}
}

@ -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

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

@ -30,7 +30,8 @@ namespace SFO
mWantMouseVisible(false), mWantMouseVisible(false),
mAllowGrab(grab), mAllowGrab(grab),
mWarpX(0), mWarpX(0),
mWarpY(0) mWarpY(0),
mFirstMouseMove(true)
{ {
_setupOISKeys(); _setupOISKeys();
} }
@ -316,6 +317,13 @@ namespace SFO
pack_evt.y = mMouseY = evt.motion.y; pack_evt.y = mMouseY = evt.motion.y;
pack_evt.xrel = evt.motion.xrel; pack_evt.xrel = evt.motion.xrel;
pack_evt.yrel = evt.motion.yrel; 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) else if(evt.type == SDL_MOUSEWHEEL)
{ {

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

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

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

@ -395,17 +395,9 @@
<Property key="FontName" value="Default"/> <Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Center"/> <Property key="TextAlign" value="Center"/>
<Child type="Widget" skin="DialogBG" offset="0 0 88 20" align="Stretch"/> <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="TextBox" skin="SandText" offset="2 0 84 20" align="Stretch" name="Client"/> <Child type="Widget" skin="HB_ALL" offset="0 0 30 20" align="Right" name="Right"/>
<!-- 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"/>
</Resource> </Resource>
<!-- ---------------------------------------------------- <!-- ----------------------------------------------------
@ -420,7 +412,8 @@
<Property key="Snap" value="true"/> <Property key="Snap" value="true"/>
<Property key="MinSize" value="64 64"/> <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 --> <!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action"> <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"/> <Property key="Scale" value="0 0 1 1"/>
</Child> </Child>
<!-- Caption --> <Child type="WindowCaption" skin="MW_Caption" offset="4 4 248 20" align="HStretch Top" name="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> </Child>
<!-- This invisible button makes it possible to move the <!-- This invisible button makes it possible to move the
@ -559,7 +547,8 @@
<Property key="Snap" value="true"/> <Property key="Snap" value="true"/>
<Property key="MinSize" value="64 64"/> <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 --> <!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action"> <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="Snap" value="true"/>
<Property key="MinSize" value="64 64"/> <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 --> <!-- Outer Borders -->
<Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action"> <Child type="Widget" skin="TB_T" offset="14 0 228 4" align="Top HStretch" name="Action">
@ -814,12 +804,7 @@
</Child> </Child>
<!-- Caption --> <!-- Caption -->
<Child type="WindowCaption" skin="MW_Caption" offset="4 4 228 20" align="HStretch Top" name="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> </Child>
<!-- This invisible button makes it possible to move the <!-- This invisible button makes it possible to move the

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

@ -46,27 +46,6 @@ namespace GUI
mMainWidget->setCoord(x,y,w,h); 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) void Layout::setVisible(bool b)
{ {
mMainWidget->setVisible(b); mMainWidget->setVisible(b);
@ -82,7 +61,6 @@ namespace GUI
void Layout::setTitle(const std::string& title) void Layout::setTitle(const std::string& title)
{ {
static_cast<MyGUI::Window*>(mMainWidget)->setCaptionWithReplacing(title); static_cast<MyGUI::Window*>(mMainWidget)->setCaptionWithReplacing(title);
adjustWindowCaption();
} }
MyGUI::Widget* Layout::getWidget(const std::string &_name) MyGUI::Widget* Layout::getWidget(const std::string &_name)

@ -45,10 +45,6 @@ namespace GUI
public: public:
void setCoord(int x, int y, int w, int h); 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); virtual void setVisible(bool b);
void setText(const std::string& name, const std::string& caption); void setText(const std::string& name, const std::string& caption);

Loading…
Cancel
Save