Merge remote-tracking branch 'scrawl/master'

openmw-35
Marc Zinnschlag 10 years ago
commit 1150b41c18

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -6,13 +6,13 @@
<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>
@ -22,13 +22,13 @@
<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>

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

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

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

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

Loading…
Cancel
Save