mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-04 03:40:14 +00:00
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
eb6e1576be
@ -969,6 +969,9 @@ namespace MWClass
|
|||||||
|
|
||||||
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
|
if(getEncumbrance(ptr) > getCapacity(ptr))
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||||
const GMST& gmst = getGmst();
|
const GMST& gmst = getGmst();
|
||||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||||
|
@ -533,9 +533,11 @@ namespace MWGui
|
|||||||
|
|
||||||
if (mGoodbye)
|
if (mGoodbye)
|
||||||
{
|
{
|
||||||
|
Goodbye* link = new Goodbye();
|
||||||
|
mLinks.push_back(link);
|
||||||
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
|
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString();
|
||||||
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
|
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, linkNormal, linkHot, linkActive,
|
||||||
TypesetBook::InteractiveId(mLinks.back()));
|
TypesetBook::InteractiveId(link));
|
||||||
typesetter->lineBreak();
|
typesetter->lineBreak();
|
||||||
typesetter->write(questionStyle, to_utf8_span(goodbye.c_str()));
|
typesetter->write(questionStyle, to_utf8_span(goodbye.c_str()));
|
||||||
}
|
}
|
||||||
@ -654,7 +656,6 @@ namespace MWGui
|
|||||||
|
|
||||||
void DialogueWindow::goodbye()
|
void DialogueWindow::goodbye()
|
||||||
{
|
{
|
||||||
mLinks.push_back(new Goodbye());
|
|
||||||
mGoodbye = true;
|
mGoodbye = true;
|
||||||
mEnabled = false;
|
mEnabled = false;
|
||||||
updateHistory();
|
updateHistory();
|
||||||
|
@ -128,11 +128,17 @@ namespace MWGui
|
|||||||
setRaceId(proto.mRace);
|
setRaceId(proto.mRace);
|
||||||
recountParts();
|
recountParts();
|
||||||
|
|
||||||
std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2);
|
for (unsigned int i=0; i<mAvailableHeads.size(); ++i)
|
||||||
mFaceIndex = boost::lexical_cast<int>(index) - 1;
|
{
|
||||||
|
if (mAvailableHeads[i] == proto.mHead)
|
||||||
|
mFaceIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
index = proto.mHair.substr(proto.mHair.size() - 2, 2);
|
for (unsigned int i=0; i<mAvailableHairs.size(); ++i)
|
||||||
mHairIndex = boost::lexical_cast<int>(index) - 1;
|
{
|
||||||
|
if (mAvailableHairs[i] == proto.mHair)
|
||||||
|
mHairIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
mPreviewImage->setImageTexture (textureName);
|
mPreviewImage->setImageTexture (textureName);
|
||||||
|
|
||||||
@ -307,7 +313,15 @@ namespace MWGui
|
|||||||
record.mHead = mAvailableHeads[mFaceIndex];
|
record.mHead = mAvailableHeads[mFaceIndex];
|
||||||
record.mHair = mAvailableHairs[mHairIndex];
|
record.mHair = mAvailableHairs[mHairIndex];
|
||||||
|
|
||||||
mPreview->setPrototype(record);
|
try
|
||||||
|
{
|
||||||
|
mPreview->setPrototype(record);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error creating preview: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
mPreviewDirty = true;
|
mPreviewDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,6 +371,7 @@ namespace MWInput
|
|||||||
{
|
{
|
||||||
mPlayer->setUpDown (1);
|
mPlayer->setUpDown (1);
|
||||||
triedToMove = true;
|
triedToMove = true;
|
||||||
|
mOverencumberedMessageDelay = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAlwaysRunActive)
|
if (mAlwaysRunActive)
|
||||||
@ -623,7 +624,9 @@ namespace MWInput
|
|||||||
if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change
|
if (arg.zrel && mControlSwitch["playerviewswitch"] && mControlSwitch["playercontrols"]) //Check to make sure you are allowed to zoomout and there is a change
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
|
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
|
||||||
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
|
|
||||||
|
if (Settings::Manager::getBool("allow third person zoom", "Input"))
|
||||||
|
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "../mwworld/actionequip.hpp"
|
#include "../mwworld/actionequip.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
#include "../mwmechanics/spellcasting.hpp"
|
||||||
|
|
||||||
#include <components/esm/loadench.hpp>
|
#include <components/esm/loadench.hpp>
|
||||||
#include <components/esm/loadmgef.hpp>
|
#include <components/esm/loadmgef.hpp>
|
||||||
@ -166,6 +167,9 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
|
||||||
|
if (MWMechanics::getSpellSuccessChance(spell, actor) == 0)
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ namespace MWMechanics
|
|||||||
/** \return If the actor has arrived at his destination **/
|
/** \return If the actor has arrived at his destination **/
|
||||||
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
|
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
|
||||||
|
|
||||||
|
// TODO: all this does not belong here, move into temporary storage
|
||||||
PathFinder mPathFinder;
|
PathFinder mPathFinder;
|
||||||
ObstacleCheck mObstacleCheck;
|
ObstacleCheck mObstacleCheck;
|
||||||
|
|
||||||
|
@ -57,6 +57,8 @@ namespace MWMechanics
|
|||||||
bool mWalking;
|
bool mWalking;
|
||||||
|
|
||||||
unsigned short mPlayedIdle;
|
unsigned short mPlayedIdle;
|
||||||
|
|
||||||
|
PathFinder mPathFinder;
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage():
|
||||||
mTargetAngle(0),
|
mTargetAngle(0),
|
||||||
@ -211,9 +213,9 @@ namespace MWMechanics
|
|||||||
// Are we there yet?
|
// Are we there yet?
|
||||||
bool& chooseAction = storage.mChooseAction;
|
bool& chooseAction = storage.mChooseAction;
|
||||||
if(walking &&
|
if(walking &&
|
||||||
mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||||
{
|
{
|
||||||
stopWalking(actor);
|
stopWalking(actor, storage);
|
||||||
moveNow = false;
|
moveNow = false;
|
||||||
walking = false;
|
walking = false;
|
||||||
chooseAction = true;
|
chooseAction = true;
|
||||||
@ -225,7 +227,7 @@ namespace MWMechanics
|
|||||||
if(walking) // have not yet reached the destination
|
if(walking) // have not yet reached the destination
|
||||||
{
|
{
|
||||||
// turn towards the next point in mPath
|
// turn towards the next point in mPath
|
||||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
zTurn(actor, Ogre::Degree(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||||
|
|
||||||
// Returns true if evasive action needs to be taken
|
// Returns true if evasive action needs to be taken
|
||||||
@ -236,9 +238,9 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
// remove allowed points then select another random destination
|
// remove allowed points then select another random destination
|
||||||
mTrimCurrentNode = true;
|
mTrimCurrentNode = true;
|
||||||
trimAllowedNodes(mAllowedNodes, mPathFinder);
|
trimAllowedNodes(mAllowedNodes, storage.mPathFinder);
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
mPathFinder.clearPath();
|
storage.mPathFinder.clearPath();
|
||||||
walking = false;
|
walking = false;
|
||||||
moveNow = true;
|
moveNow = true;
|
||||||
}
|
}
|
||||||
@ -249,7 +251,7 @@ namespace MWMechanics
|
|||||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
|
||||||
// change the angle a bit, too
|
// change the angle a bit, too
|
||||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
zTurn(actor, Ogre::Degree(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
||||||
}
|
}
|
||||||
mStuckCount++; // TODO: maybe no longer needed
|
mStuckCount++; // TODO: maybe no longer needed
|
||||||
}
|
}
|
||||||
@ -260,7 +262,7 @@ namespace MWMechanics
|
|||||||
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
//std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl;
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
|
|
||||||
stopWalking(actor);
|
stopWalking(actor, storage);
|
||||||
moveNow = false;
|
moveNow = false;
|
||||||
walking = false;
|
walking = false;
|
||||||
chooseAction = true;
|
chooseAction = true;
|
||||||
@ -280,6 +282,58 @@ namespace MWMechanics
|
|||||||
rotate = false;
|
rotate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if idle animation finished
|
||||||
|
short unsigned& playedIdle = storage.mPlayedIdle;
|
||||||
|
GreetingState& greetingState = storage.mSaidGreeting;
|
||||||
|
if(idleNow && !checkIdle(actor, playedIdle) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||||
|
{
|
||||||
|
playedIdle = 0;
|
||||||
|
idleNow = false;
|
||||||
|
chooseAction = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
if(chooseAction)
|
||||||
|
{
|
||||||
|
playedIdle = 0;
|
||||||
|
getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection
|
||||||
|
|
||||||
|
if(!playedIdle && mDistance)
|
||||||
|
{
|
||||||
|
chooseAction = false;
|
||||||
|
moveNow = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
|
||||||
|
MWWorld::TimeStamp currentTime = world->getTimeStamp();
|
||||||
|
mStartTime = currentTime;
|
||||||
|
playIdle(actor, playedIdle);
|
||||||
|
chooseAction = false;
|
||||||
|
idleNow = true;
|
||||||
|
|
||||||
|
// Play idle voiced dialogue entries randomly
|
||||||
|
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||||
|
if (hello > 0)
|
||||||
|
{
|
||||||
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
|
||||||
|
// Don't bother if the player is out of hearing range
|
||||||
|
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
|
||||||
|
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
||||||
|
|
||||||
|
// Only say Idle voices when player is in LOS
|
||||||
|
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
||||||
|
// voices going through walls?
|
||||||
|
if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500
|
||||||
|
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float& lastReaction = storage.mReaction;
|
float& lastReaction = storage.mReaction;
|
||||||
lastReaction += duration;
|
lastReaction += duration;
|
||||||
if(lastReaction < REACTION_INTERVAL)
|
if(lastReaction < REACTION_INTERVAL)
|
||||||
@ -291,7 +345,6 @@ namespace MWMechanics
|
|||||||
|
|
||||||
// NOTE: everything below get updated every REACTION_INTERVAL seconds
|
// NOTE: everything below get updated every REACTION_INTERVAL seconds
|
||||||
|
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
||||||
if(mDuration)
|
if(mDuration)
|
||||||
{
|
{
|
||||||
// End package if duration is complete or mid-night hits:
|
// End package if duration is complete or mid-night hits:
|
||||||
@ -300,7 +353,7 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
if(!mRepeat)
|
if(!mRepeat)
|
||||||
{
|
{
|
||||||
stopWalking(actor);
|
stopWalking(actor, storage);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -310,7 +363,7 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
if(!mRepeat)
|
if(!mRepeat)
|
||||||
{
|
{
|
||||||
stopWalking(actor);
|
stopWalking(actor, storage);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -411,7 +464,7 @@ namespace MWMechanics
|
|||||||
chooseAction = false;
|
chooseAction = false;
|
||||||
idleNow = false;
|
idleNow = false;
|
||||||
|
|
||||||
if (!mPathFinder.isPathConstructed())
|
if (!storage.mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
Ogre::Vector3 destNodePos = mReturnPosition;
|
Ogre::Vector3 destNodePos = mReturnPosition;
|
||||||
|
|
||||||
@ -427,9 +480,9 @@ namespace MWMechanics
|
|||||||
start.mZ = pos.pos[2];
|
start.mZ = pos.pos[2];
|
||||||
|
|
||||||
// don't take shortcuts for wandering
|
// don't take shortcuts for wandering
|
||||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||||
|
|
||||||
if(mPathFinder.isPathConstructed())
|
if(storage.mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
moveNow = false;
|
moveNow = false;
|
||||||
walking = true;
|
walking = true;
|
||||||
@ -437,48 +490,6 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AiWander::GreetingState& greetingState = storage.mSaidGreeting;
|
|
||||||
short unsigned& playedIdle = storage.mPlayedIdle;
|
|
||||||
if(chooseAction)
|
|
||||||
{
|
|
||||||
playedIdle = 0;
|
|
||||||
getRandomIdle(playedIdle); // NOTE: sets mPlayedIdle with a random selection
|
|
||||||
|
|
||||||
if(!playedIdle && mDistance)
|
|
||||||
{
|
|
||||||
chooseAction = false;
|
|
||||||
moveNow = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
|
|
||||||
MWWorld::TimeStamp currentTime = world->getTimeStamp();
|
|
||||||
mStartTime = currentTime;
|
|
||||||
playIdle(actor, playedIdle);
|
|
||||||
chooseAction = false;
|
|
||||||
idleNow = true;
|
|
||||||
|
|
||||||
// Play idle voiced dialogue entries randomly
|
|
||||||
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
|
||||||
if (hello > 0)
|
|
||||||
{
|
|
||||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
|
||||||
|
|
||||||
// Don't bother if the player is out of hearing range
|
|
||||||
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
|
|
||||||
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
|
|
||||||
|
|
||||||
// Only say Idle voices when player is in LOS
|
|
||||||
// A bit counterintuitive, likely vanilla did this to reduce the appearance of
|
|
||||||
// voices going through walls?
|
|
||||||
if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500
|
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor))
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow interrupting a walking actor to trigger a greeting
|
// Allow interrupting a walking actor to trigger a greeting
|
||||||
if(idleNow || walking)
|
if(idleNow || walking)
|
||||||
{
|
{
|
||||||
@ -494,7 +505,7 @@ namespace MWMechanics
|
|||||||
Ogre::Vector3 playerPos(player.getRefData().getPosition().pos);
|
Ogre::Vector3 playerPos(player.getRefData().getPosition().pos);
|
||||||
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
|
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
|
||||||
float playerDistSqr = playerPos.squaredDistance(actorPos);
|
float playerDistSqr = playerPos.squaredDistance(actorPos);
|
||||||
|
|
||||||
int& greetingTimer = storage.mGreetingTimer;
|
int& greetingTimer = storage.mGreetingTimer;
|
||||||
if (greetingState == Greet_None)
|
if (greetingState == Greet_None)
|
||||||
{
|
{
|
||||||
@ -517,7 +528,7 @@ namespace MWMechanics
|
|||||||
|
|
||||||
if(walking)
|
if(walking)
|
||||||
{
|
{
|
||||||
stopWalking(actor);
|
stopWalking(actor, storage);
|
||||||
moveNow = false;
|
moveNow = false;
|
||||||
walking = false;
|
walking = false;
|
||||||
mObstacleCheck.clear();
|
mObstacleCheck.clear();
|
||||||
@ -554,20 +565,12 @@ namespace MWMechanics
|
|||||||
if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset)
|
if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset)
|
||||||
greetingState = Greet_None;
|
greetingState = Greet_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if idle animation finished
|
|
||||||
if(!checkIdle(actor, playedIdle) && (playerDistSqr > helloDistance*helloDistance || greetingState == MWMechanics::AiWander::Greet_Done))
|
|
||||||
{
|
|
||||||
playedIdle = 0;
|
|
||||||
idleNow = false;
|
|
||||||
chooseAction = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(moveNow && mDistance)
|
if(moveNow && mDistance)
|
||||||
{
|
{
|
||||||
// Construct a new path if there isn't one
|
// Construct a new path if there isn't one
|
||||||
if(!mPathFinder.isPathConstructed())
|
if(!storage.mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
assert(mAllowedNodes.size());
|
assert(mAllowedNodes.size());
|
||||||
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
|
unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
|
||||||
@ -589,16 +592,16 @@ namespace MWMechanics
|
|||||||
start.mZ = pos.pos[2];
|
start.mZ = pos.pos[2];
|
||||||
|
|
||||||
// don't take shortcuts for wandering
|
// don't take shortcuts for wandering
|
||||||
mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false);
|
||||||
|
|
||||||
if(mPathFinder.isPathConstructed())
|
if(storage.mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
// buildPath inserts dest in case it is not a pathgraph point
|
// buildPath inserts dest in case it is not a pathgraph point
|
||||||
// index which is a duplicate for AiWander. However below code
|
// index which is a duplicate for AiWander. However below code
|
||||||
// does not work since getPath() returns a copy of path not a
|
// does not work since getPath() returns a copy of path not a
|
||||||
// reference
|
// reference
|
||||||
//if(mPathFinder.getPathSize() > 1)
|
//if(storage.mPathFinder.getPathSize() > 1)
|
||||||
//mPathFinder.getPath().pop_back();
|
//storage.mPathFinder.getPath().pop_back();
|
||||||
|
|
||||||
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
|
||||||
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
|
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
|
||||||
@ -616,7 +619,7 @@ namespace MWMechanics
|
|||||||
// Choose a different node and delete this one from possible nodes because it is uncreachable:
|
// Choose a different node and delete this one from possible nodes because it is uncreachable:
|
||||||
else
|
else
|
||||||
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
|
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // AiWander package not yet completed
|
return false; // AiWander package not yet completed
|
||||||
@ -653,9 +656,9 @@ namespace MWMechanics
|
|||||||
return TypeIdWander;
|
return TypeIdWander;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::stopWalking(const MWWorld::Ptr& actor)
|
void AiWander::stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
mPathFinder.clearPath();
|
storage.mPathFinder.clearPath();
|
||||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
struct AiWanderStorage;
|
||||||
|
|
||||||
/// \brief Causes the Actor to wander within a specified range
|
/// \brief Causes the Actor to wander within a specified range
|
||||||
class AiWander : public AiPackage
|
class AiWander : public AiPackage
|
||||||
{
|
{
|
||||||
@ -65,7 +67,7 @@ namespace MWMechanics
|
|||||||
// NOTE: mDistance and mDuration must be set already
|
// NOTE: mDistance and mDuration must be set already
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void stopWalking(const MWWorld::Ptr& actor);
|
void stopWalking(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
void getRandomIdle(unsigned short& playedIdle);
|
void getRandomIdle(unsigned short& playedIdle);
|
||||||
@ -101,7 +103,6 @@ namespace MWMechanics
|
|||||||
void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
|
void trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
|
||||||
const PathFinder& pathfinder);
|
const PathFinder& pathfinder);
|
||||||
|
|
||||||
// PathFinder mPathFinder;
|
|
||||||
|
|
||||||
// ObstacleCheck mObstacleCheck;
|
// ObstacleCheck mObstacleCheck;
|
||||||
float mDoorCheckDuration;
|
float mDoorCheckDuration;
|
||||||
|
@ -1414,29 +1414,32 @@ void CharacterController::update(float duration)
|
|||||||
{
|
{
|
||||||
// Started a jump.
|
// Started a jump.
|
||||||
float z = cls.getJump(mPtr);
|
float z = cls.getJump(mPtr);
|
||||||
if(vec.x == 0 && vec.y == 0)
|
if (z > 0)
|
||||||
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
|
if(vec.x == 0 && vec.y == 0)
|
||||||
vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f;
|
vec = Ogre::Vector3(0.0f, 0.0f, z);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
|
||||||
|
vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance acrobatics
|
||||||
|
if (mPtr.getRefData().getHandle() == "player")
|
||||||
|
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
|
||||||
|
|
||||||
|
// decrease fatigue
|
||||||
|
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||||
|
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
|
||||||
|
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
|
||||||
|
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
|
||||||
|
if (normalizedEncumbrance > 1)
|
||||||
|
normalizedEncumbrance = 1;
|
||||||
|
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
|
||||||
|
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
|
||||||
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
||||||
|
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance acrobatics
|
|
||||||
if (mPtr.getRefData().getHandle() == "player")
|
|
||||||
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
|
|
||||||
|
|
||||||
// decrease fatigue
|
|
||||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
|
||||||
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
|
|
||||||
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
|
|
||||||
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
|
|
||||||
if (normalizedEncumbrance > 1)
|
|
||||||
normalizedEncumbrance = 1;
|
|
||||||
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
|
|
||||||
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
|
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
|
||||||
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
|
||||||
}
|
}
|
||||||
else if(mJumpState == JumpState_InAir)
|
else if(mJumpState == JumpState_InAir)
|
||||||
{
|
{
|
||||||
|
@ -147,6 +147,11 @@ namespace MWMechanics
|
|||||||
|
|
||||||
if (value != currentValue)
|
if (value != currentValue)
|
||||||
{
|
{
|
||||||
|
if(!mIsWerewolf)
|
||||||
|
mAttributes[index] = value;
|
||||||
|
else
|
||||||
|
mWerewolfAttributes[index] = value;
|
||||||
|
|
||||||
if (index == ESM::Attribute::Intelligence)
|
if (index == ESM::Attribute::Intelligence)
|
||||||
mRecalcMagicka = true;
|
mRecalcMagicka = true;
|
||||||
else if (index == ESM::Attribute::Strength ||
|
else if (index == ESM::Attribute::Strength ||
|
||||||
@ -164,11 +169,6 @@ namespace MWMechanics
|
|||||||
setFatigue(fatigue);
|
setFatigue(fatigue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mIsWerewolf)
|
|
||||||
mAttributes[index] = value;
|
|
||||||
else
|
|
||||||
mWerewolfAttributes[index] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureStats::setHealth(const DynamicStat<float> &value)
|
void CreatureStats::setHealth(const DynamicStat<float> &value)
|
||||||
|
@ -930,20 +930,6 @@ namespace MWMechanics
|
|||||||
|
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
// What amount of alarm did this crime generate?
|
|
||||||
int alarm = 0;
|
|
||||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
|
||||||
else if (type == OT_Pickpocket)
|
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmPickPocket")->getInt();
|
|
||||||
else if (type == OT_Assault)
|
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmAttack")->getInt();
|
|
||||||
else if (type == OT_Murder)
|
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
|
||||||
else if (type == OT_Theft)
|
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
|
||||||
|
|
||||||
bool reported = false;
|
|
||||||
|
|
||||||
// Find all the actors within the alarm radius
|
// Find all the actors within the alarm radius
|
||||||
std::vector<MWWorld::Ptr> neighbors;
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
@ -960,6 +946,8 @@ namespace MWMechanics
|
|||||||
bool victimAware = false;
|
bool victimAware = false;
|
||||||
|
|
||||||
// Find actors who directly witnessed the crime
|
// Find actors who directly witnessed the crime
|
||||||
|
bool crimeSeen = false;
|
||||||
|
bool reported = false;
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||||
{
|
{
|
||||||
if (*it == player)
|
if (*it == player)
|
||||||
@ -987,15 +975,17 @@ namespace MWMechanics
|
|||||||
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Will the witness report the crime?
|
crimeSeen = true;
|
||||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
}
|
||||||
{
|
|
||||||
reported = true;
|
// Will the witness report the crime?
|
||||||
}
|
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
|
||||||
|
{
|
||||||
|
reported = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reported)
|
if (crimeSeen && reported)
|
||||||
reportCrime(player, victim, type, arg);
|
reportCrime(player, victim, type, arg);
|
||||||
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
||||||
startCombat(victim, player);
|
startCombat(victim, player);
|
||||||
|
@ -574,7 +574,8 @@ float Animation::getVelocity(const std::string &groupname) const
|
|||||||
|
|
||||||
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
|
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
|
||||||
{
|
{
|
||||||
if(skelsrc->hasBone(bone->getName()))
|
if(bone->getName() != " " // really should be != "", but see workaround in skeleton.cpp for empty node names
|
||||||
|
&& skelsrc->hasBone(bone->getName()))
|
||||||
{
|
{
|
||||||
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
|
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
|
||||||
if(!srcbone->getParent() || !bone->getParent())
|
if(!srcbone->getParent() || !bone->getParent())
|
||||||
|
@ -115,8 +115,8 @@ namespace MWRender
|
|||||||
|
|
||||||
void CharacterPreview::rebuild()
|
void CharacterPreview::rebuild()
|
||||||
{
|
{
|
||||||
assert(mAnimation);
|
|
||||||
delete mAnimation;
|
delete mAnimation;
|
||||||
|
mAnimation = NULL;
|
||||||
mAnimation = new NpcAnimation(mCharacter, mNode,
|
mAnimation = new NpcAnimation(mCharacter, mNode,
|
||||||
0, true, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
|
0, true, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
|
||||||
|
|
||||||
@ -187,6 +187,9 @@ namespace MWRender
|
|||||||
|
|
||||||
void InventoryPreview::update()
|
void InventoryPreview::update()
|
||||||
{
|
{
|
||||||
|
if (!mAnimation)
|
||||||
|
return;
|
||||||
|
|
||||||
mAnimation->updateParts();
|
mAnimation->updateParts();
|
||||||
|
|
||||||
MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter);
|
MWWorld::InventoryStore &inv = mCharacter.getClass().getInventoryStore(mCharacter);
|
||||||
|
@ -60,6 +60,21 @@ std::string getVampireHead(const std::string& race, bool female)
|
|||||||
return "meshes\\" + sVampireMapping[thisCombination]->mModel;
|
return "meshes\\" + sVampireMapping[thisCombination]->mModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSkinned (NifOgre::ObjectScenePtr scene)
|
||||||
|
{
|
||||||
|
if (scene->mSkelBase == NULL)
|
||||||
|
return false;
|
||||||
|
for(size_t j = 0; j < scene->mEntities.size(); j++)
|
||||||
|
{
|
||||||
|
Ogre::Entity *ent = scene->mEntities[j];
|
||||||
|
if(scene->mSkelBase != ent && ent->hasSkeleton())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -611,10 +626,11 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||||||
for(;ctrl != mObjectParts[i]->mControllers.end();++ctrl)
|
for(;ctrl != mObjectParts[i]->mControllers.end();++ctrl)
|
||||||
ctrl->update();
|
ctrl->update();
|
||||||
|
|
||||||
Ogre::Entity *ent = mObjectParts[i]->mSkelBase;
|
if (!isSkinned(mObjectParts[i]))
|
||||||
if(!ent) continue;
|
continue;
|
||||||
updateSkeletonInstance(baseinst, ent->getSkeleton());
|
|
||||||
ent->getAllAnimationStates()->_notifyDirty();
|
updateSkeletonInstance(baseinst, mObjectParts[i]->mSkelBase->getSkeleton());
|
||||||
|
mObjectParts[i]->mSkelBase->getAllAnimationStates()->_notifyDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -660,7 +676,15 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
|||||||
removeIndividualPart(type);
|
removeIndividualPart(type);
|
||||||
mPartslots[type] = group;
|
mPartslots[type] = group;
|
||||||
mPartPriorities[type] = priority;
|
mPartPriorities[type] = priority;
|
||||||
mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor);
|
try
|
||||||
|
{
|
||||||
|
mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error adding NPC part: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mSoundsDisabled)
|
if (!mSoundsDisabled)
|
||||||
{
|
{
|
||||||
@ -697,7 +721,8 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
if (isSkinned(mObjectParts[type]))
|
||||||
|
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[type]->mControllers.begin());
|
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[type]->mControllers.begin());
|
||||||
|
@ -34,6 +34,41 @@
|
|||||||
using namespace MWRender;
|
using namespace MWRender;
|
||||||
using namespace Ogre;
|
using namespace Ogre;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void setAlpha (NifOgre::ObjectScenePtr scene, Ogre::MovableObject* movable, float alpha)
|
||||||
|
{
|
||||||
|
Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(movable);
|
||||||
|
Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator();
|
||||||
|
while(techs.hasMoreElements())
|
||||||
|
{
|
||||||
|
Ogre::Technique *tech = techs.getNext();
|
||||||
|
Ogre::Technique::PassIterator passes = tech->getPassIterator();
|
||||||
|
while(passes.hasMoreElements())
|
||||||
|
{
|
||||||
|
Ogre::Pass *pass = passes.getNext();
|
||||||
|
Ogre::ColourValue diffuse = pass->getDiffuse();
|
||||||
|
diffuse.a = alpha;
|
||||||
|
pass->setDiffuse(diffuse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAlpha (NifOgre::ObjectScenePtr scene, float alpha)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < scene->mParticles.size(); ++i)
|
||||||
|
setAlpha(scene, scene->mParticles[i], alpha);
|
||||||
|
for(size_t i = 0; i < scene->mEntities.size(); ++i)
|
||||||
|
{
|
||||||
|
if (scene->mEntities[i] != scene->mSkelBase)
|
||||||
|
setAlpha(scene, scene->mEntities[i], alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
BillboardObject::BillboardObject( const String& textureName,
|
BillboardObject::BillboardObject( const String& textureName,
|
||||||
const float initialSize,
|
const float initialSize,
|
||||||
const Vector3& position,
|
const Vector3& position,
|
||||||
@ -660,6 +695,11 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
|||||||
mSun->setVisibility(weather.mGlareView * strength);
|
mSun->setVisibility(weather.mGlareView * strength);
|
||||||
|
|
||||||
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
||||||
|
|
||||||
|
if (mParticle.get())
|
||||||
|
setAlpha(mParticle, weather.mEffectFade);
|
||||||
|
for (std::map<Ogre::SceneNode*, NifOgre::ObjectScenePtr>::iterator it = mRainModels.begin(); it != mRainModels.end(); ++it)
|
||||||
|
setAlpha(it->second, weather.mEffectFade);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkyManager::setGlare(const float glare)
|
void SkyManager::setGlare(const float glare)
|
||||||
|
@ -210,6 +210,12 @@ namespace MWScript
|
|||||||
{
|
{
|
||||||
bool state = MWBase::Environment::get().getWindowManager()->toggleGui();
|
bool state = MWBase::Environment::get().getWindowManager()->toggleGui();
|
||||||
runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off");
|
runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off");
|
||||||
|
|
||||||
|
if (!state)
|
||||||
|
{
|
||||||
|
while (MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_None) // don't use isGuiMode, or we get an infinite loop for modal message boxes!
|
||||||
|
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -319,12 +319,11 @@ namespace MWScript
|
|||||||
ptr = MWWorld::Ptr(ptr.getBase(), store);
|
ptr = MWWorld::Ptr(ptr.getBase(), store);
|
||||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||||
{
|
// except for when you position the player, then degrees must be used.
|
||||||
ax = ax/60.;
|
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||||
ay = ay/60.;
|
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
zRot = zRot/60.;
|
zRot = zRot/60.;
|
||||||
}
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||||
|
|
||||||
ptr.getClass().adjustPosition(ptr, false);
|
ptr.getClass().adjustPosition(ptr, false);
|
||||||
@ -378,12 +377,11 @@ namespace MWScript
|
|||||||
|
|
||||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||||
{
|
// except for when you position the player, then degrees must be used.
|
||||||
ax = ax/60.;
|
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||||
ay = ay/60.;
|
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
zRot = zRot/60.;
|
zRot = zRot/60.;
|
||||||
}
|
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
||||||
ptr.getClass().adjustPosition(ptr, false);
|
ptr.getClass().adjustPosition(ptr, false);
|
||||||
}
|
}
|
||||||
|
@ -876,8 +876,36 @@ namespace MWWorld
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(ESM::Pathgrid());
|
|
||||||
mStatic.back().load(esm);
|
ESM::Pathgrid pathgrid;
|
||||||
|
pathgrid.load(esm);
|
||||||
|
|
||||||
|
// Try to overwrite existing record
|
||||||
|
// Can't use search() because we aren't sorted yet
|
||||||
|
if (!pathgrid.mCell.empty())
|
||||||
|
{
|
||||||
|
for (std::vector<ESM::Pathgrid>::iterator it = mStatic.begin(); it != mStatic.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it).mCell == pathgrid.mCell)
|
||||||
|
{
|
||||||
|
(*it) = pathgrid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (std::vector<ESM::Pathgrid>::iterator it = mStatic.begin(); it != mStatic.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it).mData.mX == pathgrid.mData.mX && (*it).mData.mY == pathgrid.mData.mY)
|
||||||
|
{
|
||||||
|
(*it) = pathgrid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mStatic.push_back(pathgrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getSize() const {
|
size_t getSize() const {
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwsound/sound.hpp"
|
||||||
|
|
||||||
#include "../mwrender/renderingmanager.hpp"
|
#include "../mwrender/renderingmanager.hpp"
|
||||||
|
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
@ -152,12 +154,12 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa
|
|||||||
setFallbackWeather(foggy,"foggy");
|
setFallbackWeather(foggy,"foggy");
|
||||||
|
|
||||||
Weather thunderstorm;
|
Weather thunderstorm;
|
||||||
thunderstorm.mRainLoopSoundID = "rain heavy";
|
thunderstorm.mAmbientLoopSoundID = "rain heavy";
|
||||||
thunderstorm.mRainEffect = "meshes\\raindrop.nif";
|
thunderstorm.mRainEffect = "meshes\\raindrop.nif";
|
||||||
setFallbackWeather(thunderstorm,"thunderstorm");
|
setFallbackWeather(thunderstorm,"thunderstorm");
|
||||||
|
|
||||||
Weather rain;
|
Weather rain;
|
||||||
rain.mRainLoopSoundID = "rain";
|
rain.mAmbientLoopSoundID = "rain";
|
||||||
rain.mRainEffect = "meshes\\raindrop.nif";
|
rain.mRainEffect = "meshes\\raindrop.nif";
|
||||||
setFallbackWeather(rain,"rain");
|
setFallbackWeather(rain,"rain");
|
||||||
|
|
||||||
@ -186,7 +188,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa
|
|||||||
|
|
||||||
WeatherManager::~WeatherManager()
|
WeatherManager::~WeatherManager()
|
||||||
{
|
{
|
||||||
stopSounds(true);
|
stopSounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WeatherManager::setWeather(const String& weather, bool instant)
|
void WeatherManager::setWeather(const String& weather, bool instant)
|
||||||
@ -228,6 +230,8 @@ void WeatherManager::setResult(const String& weatherType)
|
|||||||
mResult.mCloudSpeed = current.mCloudSpeed;
|
mResult.mCloudSpeed = current.mCloudSpeed;
|
||||||
mResult.mGlareView = current.mGlareView;
|
mResult.mGlareView = current.mGlareView;
|
||||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||||
|
mResult.mAmbientSoundVolume = 1.f;
|
||||||
|
mResult.mEffectFade = 1.f;
|
||||||
mResult.mSunColor = current.mSunDiscSunsetColor;
|
mResult.mSunColor = current.mSunDiscSunsetColor;
|
||||||
|
|
||||||
mResult.mIsStorm = current.mIsStorm;
|
mResult.mIsStorm = current.mIsStorm;
|
||||||
@ -341,11 +345,30 @@ void WeatherManager::transition(float factor)
|
|||||||
|
|
||||||
mResult.mNight = current.mNight;
|
mResult.mNight = current.mNight;
|
||||||
|
|
||||||
mResult.mIsStorm = current.mIsStorm;
|
if (factor < 0.5)
|
||||||
mResult.mParticleEffect = current.mParticleEffect;
|
{
|
||||||
mResult.mRainEffect = current.mRainEffect;
|
mResult.mIsStorm = current.mIsStorm;
|
||||||
mResult.mRainSpeed = current.mRainSpeed;
|
mResult.mParticleEffect = current.mParticleEffect;
|
||||||
mResult.mRainFrequency = current.mRainFrequency;
|
mResult.mRainEffect = current.mRainEffect;
|
||||||
|
mResult.mParticleEffect = current.mParticleEffect;
|
||||||
|
mResult.mRainSpeed = current.mRainSpeed;
|
||||||
|
mResult.mRainFrequency = current.mRainFrequency;
|
||||||
|
mResult.mAmbientSoundVolume = 1-(factor*2);
|
||||||
|
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||||
|
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mResult.mIsStorm = other.mIsStorm;
|
||||||
|
mResult.mParticleEffect = other.mParticleEffect;
|
||||||
|
mResult.mRainEffect = other.mRainEffect;
|
||||||
|
mResult.mParticleEffect = other.mParticleEffect;
|
||||||
|
mResult.mRainSpeed = other.mRainSpeed;
|
||||||
|
mResult.mRainFrequency = other.mRainFrequency;
|
||||||
|
mResult.mAmbientSoundVolume = 2*(factor-0.5);
|
||||||
|
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||||
|
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WeatherManager::update(float duration, bool paused)
|
void WeatherManager::update(float duration, bool paused)
|
||||||
@ -361,7 +384,7 @@ void WeatherManager::update(float duration, bool paused)
|
|||||||
{
|
{
|
||||||
mRendering->skyDisable();
|
mRendering->skyDisable();
|
||||||
mRendering->getSkyManager()->setLightningStrength(0.f);
|
mRendering->getSkyManager()->setLightningStrength(0.f);
|
||||||
stopSounds(true);
|
stopSounds();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,40 +564,25 @@ void WeatherManager::update(float duration, bool paused)
|
|||||||
mRendering->getSkyManager()->setWeather(mResult);
|
mRendering->getSkyManager()->setWeather(mResult);
|
||||||
|
|
||||||
// Play sounds
|
// Play sounds
|
||||||
if (mNextWeather == "")
|
if (mPlayingSoundID != mResult.mAmbientLoopSoundID)
|
||||||
{
|
{
|
||||||
std::string ambientSnd = mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID;
|
stopSounds();
|
||||||
if (!ambientSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end())
|
if (!mResult.mAmbientLoopSoundID.empty())
|
||||||
{
|
mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(mResult.mAmbientLoopSoundID, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||||
mSoundsPlaying.push_back(ambientSnd);
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string rainSnd = mWeatherSettings[mCurrentWeather].mRainLoopSoundID;
|
mPlayingSoundID = mResult.mAmbientLoopSoundID;
|
||||||
if (!rainSnd.empty() && std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end())
|
|
||||||
{
|
|
||||||
mSoundsPlaying.push_back(rainSnd);
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (mAmbientSound.get())
|
||||||
stopSounds(false);
|
mAmbientSound->setVolume(mResult.mAmbientSoundVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WeatherManager::stopSounds(bool stopAll)
|
void WeatherManager::stopSounds()
|
||||||
{
|
{
|
||||||
std::vector<std::string>::iterator it = mSoundsPlaying.begin();
|
if (mAmbientSound.get())
|
||||||
while (it!=mSoundsPlaying.end())
|
|
||||||
{
|
{
|
||||||
if (stopAll ||
|
MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound);
|
||||||
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) ||
|
mAmbientSound.reset();
|
||||||
(*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID)))
|
mPlayingSoundID.clear();
|
||||||
{
|
|
||||||
MWBase::Environment::get().getSoundManager()->stopSound(*it);
|
|
||||||
it = mSoundsPlaying.erase(it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -764,7 +772,7 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type)
|
|||||||
state.load(reader);
|
state.load(reader);
|
||||||
|
|
||||||
// reset other temporary state, now that we loaded successfully
|
// reset other temporary state, now that we loaded successfully
|
||||||
stopSounds(true); // let's hope this never throws
|
stopSounds(); // let's hope this never throws
|
||||||
mRegionOverrides.clear();
|
mRegionOverrides.clear();
|
||||||
mRegionMods.clear();
|
mRegionMods.clear();
|
||||||
mThunderFlash = 0.0;
|
mThunderFlash = 0.0;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <OgreColourValue.h>
|
#include <OgreColourValue.h>
|
||||||
#include <OgreVector3.h>
|
#include <OgreVector3.h>
|
||||||
|
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
struct Region;
|
struct Region;
|
||||||
@ -61,10 +63,12 @@ namespace MWWorld
|
|||||||
bool mIsStorm;
|
bool mIsStorm;
|
||||||
|
|
||||||
std::string mAmbientLoopSoundID;
|
std::string mAmbientLoopSoundID;
|
||||||
|
float mAmbientSoundVolume;
|
||||||
|
|
||||||
std::string mParticleEffect;
|
std::string mParticleEffect;
|
||||||
|
|
||||||
std::string mRainEffect;
|
std::string mRainEffect;
|
||||||
|
float mEffectFade;
|
||||||
|
|
||||||
float mRainSpeed;
|
float mRainSpeed;
|
||||||
float mRainFrequency;
|
float mRainFrequency;
|
||||||
};
|
};
|
||||||
@ -125,9 +129,6 @@ namespace MWWorld
|
|||||||
// This is used for Blight, Ashstorm and Blizzard (Bloodmoon)
|
// This is used for Blight, Ashstorm and Blizzard (Bloodmoon)
|
||||||
std::string mAmbientLoopSoundID;
|
std::string mAmbientLoopSoundID;
|
||||||
|
|
||||||
// Rain sound effect
|
|
||||||
std::string mRainLoopSoundID;
|
|
||||||
|
|
||||||
// Is this an ash storm / blight storm? If so, the following will happen:
|
// Is this an ash storm / blight storm? If so, the following will happen:
|
||||||
// - The particles and clouds will be oriented so they appear to come from the Red Mountain.
|
// - The particles and clouds will be oriented so they appear to come from the Red Mountain.
|
||||||
// - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)
|
// - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation)
|
||||||
@ -173,7 +174,7 @@ namespace MWWorld
|
|||||||
*/
|
*/
|
||||||
void update(float duration, bool paused = false);
|
void update(float duration, bool paused = false);
|
||||||
|
|
||||||
void stopSounds(bool stopAll);
|
void stopSounds();
|
||||||
|
|
||||||
void setHour(const float hour);
|
void setHour(const float hour);
|
||||||
|
|
||||||
@ -206,6 +207,9 @@ namespace MWWorld
|
|||||||
bool mIsStorm;
|
bool mIsStorm;
|
||||||
Ogre::Vector3 mStormDirection;
|
Ogre::Vector3 mStormDirection;
|
||||||
|
|
||||||
|
MWBase::SoundPtr mAmbientSound;
|
||||||
|
std::string mPlayingSoundID;
|
||||||
|
|
||||||
MWWorld::Fallback* mFallback;
|
MWWorld::Fallback* mFallback;
|
||||||
void setFallbackWeather(Weather& weather,const std::string& name);
|
void setFallbackWeather(Weather& weather,const std::string& name);
|
||||||
MWRender::RenderingManager* mRendering;
|
MWRender::RenderingManager* mRendering;
|
||||||
@ -214,8 +218,6 @@ namespace MWWorld
|
|||||||
|
|
||||||
std::map<std::string, std::string> mRegionOverrides;
|
std::map<std::string, std::string> mRegionOverrides;
|
||||||
|
|
||||||
std::vector<std::string> mSoundsPlaying;
|
|
||||||
|
|
||||||
std::string mCurrentWeather;
|
std::string mCurrentWeather;
|
||||||
std::string mNextWeather;
|
std::string mNextWeather;
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ class NiSkinData : public Record
|
|||||||
public:
|
public:
|
||||||
struct BoneTrafo
|
struct BoneTrafo
|
||||||
{
|
{
|
||||||
Ogre::Matrix3 rotationScale; // Rotation offset from bone, non-uniform scale
|
Ogre::Matrix3 rotation; // Rotation offset from bone?
|
||||||
Ogre::Vector3 trans; // Translation
|
Ogre::Vector3 trans; // Translation
|
||||||
float scale; // Probably scale (always 1)
|
float scale; // Probably scale (always 1)
|
||||||
};
|
};
|
||||||
@ -295,7 +295,7 @@ public:
|
|||||||
|
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
trafo.rotationScale = nif->getMatrix3();
|
trafo.rotation = nif->getMatrix3();
|
||||||
trafo.trans = nif->getVector3();
|
trafo.trans = nif->getVector3();
|
||||||
trafo.scale = nif->getFloat();
|
trafo.scale = nif->getFloat();
|
||||||
|
|
||||||
@ -307,7 +307,7 @@ public:
|
|||||||
{
|
{
|
||||||
BoneInfo &bi = bones[i];
|
BoneInfo &bi = bones[i];
|
||||||
|
|
||||||
bi.trafo.rotationScale = nif->getMatrix3();
|
bi.trafo.rotation = nif->getMatrix3();
|
||||||
bi.trafo.trans = nif->getVector3();
|
bi.trafo.trans = nif->getVector3();
|
||||||
bi.trafo.scale = nif->getFloat();
|
bi.trafo.scale = nif->getFloat();
|
||||||
bi.unknown = nif->getVector4();
|
bi.unknown = nif->getVector4();
|
||||||
|
@ -76,7 +76,7 @@ Transformation NIFStream::getTrafo()
|
|||||||
{
|
{
|
||||||
Transformation t;
|
Transformation t;
|
||||||
t.pos = getVector3();
|
t.pos = getVector3();
|
||||||
t.rotationScale = getMatrix3();
|
t.rotation = getMatrix3();
|
||||||
t.scale = getFloat();
|
t.scale = getFloat();
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace Nif
|
|||||||
struct Transformation
|
struct Transformation
|
||||||
{
|
{
|
||||||
Ogre::Vector3 pos;
|
Ogre::Vector3 pos;
|
||||||
Ogre::Matrix3 rotationScale;
|
Ogre::Matrix3 rotation;
|
||||||
float scale;
|
float scale;
|
||||||
|
|
||||||
static const Transformation& getIdentity()
|
static const Transformation& getIdentity()
|
||||||
|
@ -9,10 +9,11 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||||||
const Nif::NiVertexColorProperty *&vertprop,
|
const Nif::NiVertexColorProperty *&vertprop,
|
||||||
const Nif::NiZBufferProperty *&zprop,
|
const Nif::NiZBufferProperty *&zprop,
|
||||||
const Nif::NiSpecularProperty *&specprop,
|
const Nif::NiSpecularProperty *&specprop,
|
||||||
const Nif::NiWireframeProperty *&wireprop) const
|
const Nif::NiWireframeProperty *&wireprop,
|
||||||
|
const Nif::NiStencilProperty *&stencilprop) const
|
||||||
{
|
{
|
||||||
if(parent)
|
if(parent)
|
||||||
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||||
|
|
||||||
for(size_t i = 0;i < props.length();i++)
|
for(size_t i = 0;i < props.length();i++)
|
||||||
{
|
{
|
||||||
@ -35,6 +36,8 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||||||
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
|
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
|
||||||
else if(pr->recType == Nif::RC_NiWireframeProperty)
|
else if(pr->recType == Nif::RC_NiWireframeProperty)
|
||||||
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
|
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
|
||||||
|
else if (pr->recType == Nif::RC_NiStencilProperty)
|
||||||
|
stencilprop = static_cast<const Nif::NiStencilProperty*>(pr);
|
||||||
else
|
else
|
||||||
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
|
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
|
||||||
}
|
}
|
||||||
@ -42,9 +45,8 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
|
|||||||
|
|
||||||
Ogre::Matrix4 Node::getLocalTransform() const
|
Ogre::Matrix4 Node::getLocalTransform() const
|
||||||
{
|
{
|
||||||
Ogre::Matrix4 mat4 = Ogre::Matrix4(trafo.rotationScale);
|
Ogre::Matrix4 mat4 = Ogre::Matrix4(Ogre::Matrix4::IDENTITY);
|
||||||
mat4.setTrans(trafo.pos);
|
mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation));
|
||||||
mat4.setScale(Ogre::Vector3(trafo.rotationScale[0][0], trafo.rotationScale[1][1], trafo.rotationScale[2][2]) * trafo.scale);
|
|
||||||
return mat4;
|
return mat4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,8 @@ public:
|
|||||||
const Nif::NiVertexColorProperty *&vertprop,
|
const Nif::NiVertexColorProperty *&vertprop,
|
||||||
const Nif::NiZBufferProperty *&zprop,
|
const Nif::NiZBufferProperty *&zprop,
|
||||||
const Nif::NiSpecularProperty *&specprop,
|
const Nif::NiSpecularProperty *&specprop,
|
||||||
const Nif::NiWireframeProperty *&wireprop) const;
|
const Nif::NiWireframeProperty *&wireprop,
|
||||||
|
const Nif::NiStencilProperty *&stencilprop) const;
|
||||||
|
|
||||||
Ogre::Matrix4 getLocalTransform() const;
|
Ogre::Matrix4 getLocalTransform() const;
|
||||||
Ogre::Matrix4 getWorldTransform() const;
|
Ogre::Matrix4 getWorldTransform() const;
|
||||||
|
@ -54,6 +54,28 @@ static const char *getTestMode(int mode)
|
|||||||
return "less_equal";
|
return "less_equal";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setTextureProperties(sh::MaterialInstance* material, const std::string& textureSlotName, const Nif::NiTexturingProperty::Texture& tex)
|
||||||
|
{
|
||||||
|
material->setProperty(textureSlotName + "UVSet", sh::makeProperty(new sh::IntValue(tex.uvSet)));
|
||||||
|
const std::string clampMode = textureSlotName + "ClampMode";
|
||||||
|
switch (tex.clamp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp clamp")));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp wrap")));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap clamp")));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap wrap")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
||||||
const Ogre::String &name, const Ogre::String &group,
|
const Ogre::String &name, const Ogre::String &group,
|
||||||
const Nif::NiTexturingProperty *texprop,
|
const Nif::NiTexturingProperty *texprop,
|
||||||
@ -63,6 +85,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
const Nif::NiZBufferProperty *zprop,
|
const Nif::NiZBufferProperty *zprop,
|
||||||
const Nif::NiSpecularProperty *specprop,
|
const Nif::NiSpecularProperty *specprop,
|
||||||
const Nif::NiWireframeProperty *wireprop,
|
const Nif::NiWireframeProperty *wireprop,
|
||||||
|
const Nif::NiStencilProperty *stencilprop,
|
||||||
bool &needTangents, bool particleMaterial)
|
bool &needTangents, bool particleMaterial)
|
||||||
{
|
{
|
||||||
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
|
||||||
@ -84,6 +107,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
// Default should be 1, but Bloodmoon's models are broken
|
// Default should be 1, but Bloodmoon's models are broken
|
||||||
int specFlags = 0;
|
int specFlags = 0;
|
||||||
int wireFlags = 0;
|
int wireFlags = 0;
|
||||||
|
int drawMode = 1;
|
||||||
Ogre::String texName[7];
|
Ogre::String texName[7];
|
||||||
|
|
||||||
bool vertexColour = (shapedata->colors.size() != 0);
|
bool vertexColour = (shapedata->colors.size() != 0);
|
||||||
@ -183,6 +207,20 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(stencilprop)
|
||||||
|
{
|
||||||
|
drawMode = stencilprop->data.drawMode;
|
||||||
|
if (stencilprop->data.enabled)
|
||||||
|
warn("Unhandled stencil test in "+name);
|
||||||
|
|
||||||
|
Nif::ControllerPtr ctrls = stencilprop->controller;
|
||||||
|
while(!ctrls.empty())
|
||||||
|
{
|
||||||
|
warn("Unhandled stencil controller "+ctrls->recName+" in "+name);
|
||||||
|
ctrls = ctrls->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Material
|
// Material
|
||||||
if(matprop)
|
if(matprop)
|
||||||
{
|
{
|
||||||
@ -227,8 +265,13 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
for(int i = 0;i < 7;i++)
|
for(int i = 0;i < 7;i++)
|
||||||
{
|
{
|
||||||
if(!texName[i].empty())
|
if(!texName[i].empty())
|
||||||
|
{
|
||||||
boost::hash_combine(h, texName[i]);
|
boost::hash_combine(h, texName[i]);
|
||||||
|
boost::hash_combine(h, texprop->textures[i].clamp);
|
||||||
|
boost::hash_combine(h, texprop->textures[i].uvSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
boost::hash_combine(h, drawMode);
|
||||||
boost::hash_combine(h, vertexColour);
|
boost::hash_combine(h, vertexColour);
|
||||||
boost::hash_combine(h, alphaFlags);
|
boost::hash_combine(h, alphaFlags);
|
||||||
boost::hash_combine(h, alphaTest);
|
boost::hash_combine(h, alphaTest);
|
||||||
@ -286,6 +329,13 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe")));
|
instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drawMode == 1)
|
||||||
|
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("clockwise")));
|
||||||
|
else if (drawMode == 2)
|
||||||
|
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("anticlockwise")));
|
||||||
|
else if (drawMode == 3)
|
||||||
|
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("none")));
|
||||||
|
|
||||||
instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture]));
|
instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture]));
|
||||||
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
|
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
|
||||||
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
|
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
|
||||||
@ -294,22 +344,22 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
|
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
|
||||||
{
|
{
|
||||||
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
|
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||||
instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet)));
|
setTextureProperties(instance, "diffuseMap", texprop->textures[Nif::NiTexturingProperty::BaseTexture]);
|
||||||
}
|
}
|
||||||
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
|
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
|
||||||
{
|
{
|
||||||
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
|
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||||
instance->setProperty("emissiveMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::GlowTexture].uvSet)));
|
setTextureProperties(instance, "emissiveMap", texprop->textures[Nif::NiTexturingProperty::GlowTexture]);
|
||||||
}
|
}
|
||||||
if (!texName[Nif::NiTexturingProperty::DetailTexture].empty())
|
if (!texName[Nif::NiTexturingProperty::DetailTexture].empty())
|
||||||
{
|
{
|
||||||
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
|
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||||
instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet)));
|
setTextureProperties(instance, "detailMap", texprop->textures[Nif::NiTexturingProperty::DetailTexture]);
|
||||||
}
|
}
|
||||||
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
|
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
|
||||||
{
|
{
|
||||||
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
|
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||||
instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet)));
|
setTextureProperties(instance, "darkMap", texprop->textures[Nif::NiTexturingProperty::DarkTexture]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
|
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
|
||||||
@ -332,7 +382,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
|
|||||||
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
|
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
|
||||||
|
|
||||||
// Override alpha flags based on our override list (transparency-overrides.cfg)
|
// Override alpha flags based on our override list (transparency-overrides.cfg)
|
||||||
if (!texName[0].empty())
|
if ((alphaFlags&1) && !texName[0].empty())
|
||||||
{
|
{
|
||||||
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
|
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
|
||||||
if (result.first)
|
if (result.first)
|
||||||
|
@ -18,6 +18,7 @@ namespace Nif
|
|||||||
class NiZBufferProperty;
|
class NiZBufferProperty;
|
||||||
class NiSpecularProperty;
|
class NiSpecularProperty;
|
||||||
class NiWireframeProperty;
|
class NiWireframeProperty;
|
||||||
|
class NiStencilProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NifOgre
|
namespace NifOgre
|
||||||
@ -41,6 +42,7 @@ public:
|
|||||||
const Nif::NiZBufferProperty *zprop,
|
const Nif::NiZBufferProperty *zprop,
|
||||||
const Nif::NiSpecularProperty *specprop,
|
const Nif::NiSpecularProperty *specprop,
|
||||||
const Nif::NiWireframeProperty *wireprop,
|
const Nif::NiWireframeProperty *wireprop,
|
||||||
|
const Nif::NiStencilProperty *stencilprop,
|
||||||
bool &needTangents, bool particleMaterial=false);
|
bool &needTangents, bool particleMaterial=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,10 +138,9 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||||||
const Nif::NodeList &bones = skin->bones;
|
const Nif::NodeList &bones = skin->bones;
|
||||||
for(size_t b = 0;b < bones.length();b++)
|
for(size_t b = 0;b < bones.length();b++)
|
||||||
{
|
{
|
||||||
const Ogre::Matrix3& rotationScale = data->bones[b].trafo.rotationScale;
|
Ogre::Matrix4 mat;
|
||||||
Ogre::Matrix4 mat (rotationScale);
|
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
|
||||||
mat.setTrans(data->bones[b].trafo.trans);
|
Ogre::Quaternion(data->bones[b].trafo.rotation));
|
||||||
mat.setScale(Ogre::Vector3(rotationScale[0][0], rotationScale[1][1], rotationScale[2][2]) * data->bones[b].trafo.scale);
|
|
||||||
mat = bones[b]->getWorldTransform() * mat;
|
mat = bones[b]->getWorldTransform() * mat;
|
||||||
|
|
||||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
|
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
|
||||||
@ -321,13 +320,14 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||||||
const Nif::NiZBufferProperty *zprop = NULL;
|
const Nif::NiZBufferProperty *zprop = NULL;
|
||||||
const Nif::NiSpecularProperty *specprop = NULL;
|
const Nif::NiSpecularProperty *specprop = NULL;
|
||||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||||
|
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||||
bool needTangents = false;
|
bool needTangents = false;
|
||||||
|
|
||||||
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||||
std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup,
|
std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup,
|
||||||
texprop, matprop, alphaprop,
|
texprop, matprop, alphaprop,
|
||||||
vertprop, zprop, specprop,
|
vertprop, zprop, specprop,
|
||||||
wireprop, needTangents);
|
wireprop, stencilprop, needTangents);
|
||||||
if(matname.length() > 0)
|
if(matname.length() > 0)
|
||||||
sub->setMaterialName(matname);
|
sub->setMaterialName(matname);
|
||||||
|
|
||||||
|
@ -732,7 +732,8 @@ class NIFObjectLoader
|
|||||||
const Nif::NiZBufferProperty *zprop = NULL;
|
const Nif::NiZBufferProperty *zprop = NULL;
|
||||||
const Nif::NiSpecularProperty *specprop = NULL;
|
const Nif::NiSpecularProperty *specprop = NULL;
|
||||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||||
node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||||
|
node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||||
|
|
||||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||||
@ -889,13 +890,14 @@ class NIFObjectLoader
|
|||||||
const Nif::NiZBufferProperty *zprop = NULL;
|
const Nif::NiZBufferProperty *zprop = NULL;
|
||||||
const Nif::NiSpecularProperty *specprop = NULL;
|
const Nif::NiSpecularProperty *specprop = NULL;
|
||||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||||
|
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||||
bool needTangents = false;
|
bool needTangents = false;
|
||||||
|
|
||||||
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||||
texprop, matprop, alphaprop,
|
texprop, matprop, alphaprop,
|
||||||
vertprop, zprop, specprop,
|
vertprop, zprop, specprop,
|
||||||
wireprop, needTangents,
|
wireprop, stencilprop, needTangents,
|
||||||
// MW doesn't light particles, but the MaterialProperty
|
// MW doesn't light particles, but the MaterialProperty
|
||||||
// used still has lighting, so that must be ignored.
|
// used still has lighting, so that must be ignored.
|
||||||
true));
|
true));
|
||||||
|
@ -36,17 +36,9 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||||||
if(parent) parent->addChild(bone);
|
if(parent) parent->addChild(bone);
|
||||||
mNifToOgreHandleMap[node->recIndex] = bone->getHandle();
|
mNifToOgreHandleMap[node->recIndex] = bone->getHandle();
|
||||||
|
|
||||||
// decompose the local transform into position, scale and orientation.
|
bone->setOrientation(node->trafo.rotation);
|
||||||
// this is required for cases where the rotationScale matrix includes scaling, which the NIF format allows :(
|
bone->setPosition(node->trafo.pos);
|
||||||
// the code would look a bit nicer if Ogre allowed setting the transform matrix of a Bone directly, but we can't do that.
|
bone->setScale(Ogre::Vector3(node->trafo.scale));
|
||||||
Ogre::Matrix4 mat(node->getLocalTransform());
|
|
||||||
Ogre::Vector3 position, scale;
|
|
||||||
Ogre::Quaternion orientation;
|
|
||||||
mat.decomposition(position, scale, orientation);
|
|
||||||
bone->setOrientation(orientation);
|
|
||||||
bone->setPosition(position);
|
|
||||||
bone->setScale(scale);
|
|
||||||
|
|
||||||
bone->setBindingPose();
|
bone->setBindingPose();
|
||||||
|
|
||||||
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
|
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
|
||||||
|
@ -67,12 +67,14 @@ material openmw_objects_base
|
|||||||
depth_check $depth_check
|
depth_check $depth_check
|
||||||
transparent_sorting $transparent_sorting
|
transparent_sorting $transparent_sorting
|
||||||
polygon_mode $polygon_mode
|
polygon_mode $polygon_mode
|
||||||
|
cull_hardware $cullmode
|
||||||
|
|
||||||
texture_unit diffuseMap
|
texture_unit diffuseMap
|
||||||
{
|
{
|
||||||
direct_texture $diffuseMap
|
direct_texture $diffuseMap
|
||||||
create_in_ffp $use_diffuse_map
|
create_in_ffp $use_diffuse_map
|
||||||
tex_coord_set $diffuseMapUVSet
|
tex_coord_set $diffuseMapUVSet
|
||||||
|
tex_address_mode $diffuseMapClampMode
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_unit normalMap
|
texture_unit normalMap
|
||||||
@ -89,6 +91,7 @@ material openmw_objects_base
|
|||||||
alpha_op_ex modulate src_current src_texture
|
alpha_op_ex modulate src_current src_texture
|
||||||
direct_texture $darkMap
|
direct_texture $darkMap
|
||||||
tex_coord_set $darkMapUVSet
|
tex_coord_set $darkMapUVSet
|
||||||
|
tex_address_mode $darkMapClampMode
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_unit detailMap
|
texture_unit detailMap
|
||||||
@ -97,6 +100,7 @@ material openmw_objects_base
|
|||||||
colour_op_ex modulate_x2 src_current src_texture
|
colour_op_ex modulate_x2 src_current src_texture
|
||||||
direct_texture $detailMap
|
direct_texture $detailMap
|
||||||
tex_coord_set $detailMapUVSet
|
tex_coord_set $detailMapUVSet
|
||||||
|
tex_address_mode $detailMapClampMode
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_unit emissiveMap
|
texture_unit emissiveMap
|
||||||
@ -105,6 +109,7 @@ material openmw_objects_base
|
|||||||
colour_op add
|
colour_op add
|
||||||
direct_texture $emissiveMap
|
direct_texture $emissiveMap
|
||||||
tex_coord_set $emissiveMapUVSet
|
tex_coord_set $emissiveMapUVSet
|
||||||
|
tex_address_mode $emissiveMapClampMode
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_unit envMap
|
texture_unit envMap
|
||||||
|
@ -179,6 +179,8 @@ camera y multiplier = 1.0
|
|||||||
|
|
||||||
always run = false
|
always run = false
|
||||||
|
|
||||||
|
allow third person zoom = false
|
||||||
|
|
||||||
[Game]
|
[Game]
|
||||||
# Always use the most powerful attack when striking with a weapon (chop, slash or thrust)
|
# Always use the most powerful attack when striking with a weapon (chop, slash or thrust)
|
||||||
best attack = false
|
best attack = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user