Merge pull request #447 from OpenMW/master while resolving conflicts

# Conflicts:
#	README.md
pull/448/head
David Cernat 7 years ago
commit 2a3c74bfcc

@ -1,6 +1,8 @@
0.45.0 0.45.0
------ ------
Bug #4293: Faction members are not aware of faction ownerships in barter
Bug #4426: RotateWorld behavior is incorrect
Bug #4433: Guard behaviour is incorrect with Alarm = 0
0.44.0 0.44.0
------ ------

@ -1,5 +1,22 @@
#!/bin/bash #!/bin/bash
MISSINGTOOLS=0
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
if [ $MISSINGTOOLS -ne 0 ]; then
exit 1
fi
WORKINGDIR="$(pwd)"
case "$WORKINGDIR" in
*[[:space:]]*)
echo "Error: Working directory contains spaces."
exit 1
;;
esac
set -euo pipefail set -euo pipefail
APPVEYOR=${APPVEYOR:-} APPVEYOR=${APPVEYOR:-}

@ -249,7 +249,7 @@ namespace MWBase
virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) = 0; virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) = 0;
/// Has the player stolen this item from the given owner? /// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; virtual bool isItemStolenFrom(const std::string& itemid, const MWWorld::Ptr& ptr) = 0;
virtual bool isBoundItem(const MWWorld::Ptr& item) = 0; virtual bool isBoundItem(const MWWorld::Ptr& item) = 0;
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0; virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0;

@ -650,6 +650,8 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0; virtual bool isPlayerInJail() const = 0;
virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0;
/// Return terrain height at \a worldPos position. /// Return terrain height at \a worldPos position.
virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0; virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0;

@ -339,8 +339,7 @@ namespace MWGui
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
{ {
MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();
if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr))
mPtr.getCellRef().getRefId()))
{ {
std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString(); std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)

@ -322,8 +322,7 @@ namespace MWGui
// check if the player is attempting to sell back an item stolen from this actor // check if the player is attempting to sell back an item stolen from this actor
for (std::vector<ItemStack>::iterator it = merchantBought.begin(); it != merchantBought.end(); ++it) for (std::vector<ItemStack>::iterator it = merchantBought.begin(); it != merchantBought.end(); ++it)
{ {
if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr))
mPtr.getCellRef().getRefId()))
{ {
std::string msg = gmst.find("sNotifyMessage49")->getString(); std::string msg = gmst.find("sNotifyMessage49")->getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)

@ -1027,14 +1027,26 @@ namespace MWMechanics
} }
} }
bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const std::string &ownerid) bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const MWWorld::Ptr& ptr)
{ {
StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid)); StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid));
if (it == mStolenItems.end()) if (it == mStolenItems.end())
return false; return false;
const OwnerMap& owners = it->second; const OwnerMap& owners = it->second;
const std::string ownerid = ptr.getCellRef().getRefId();
OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));
return ownerFound != owners.end(); if (ownerFound != owners.end())
return true;
const std::string factionid = ptr.getClass().getPrimaryFaction(ptr);
if (!factionid.empty())
{
OwnerMap::const_iterator factionOwnerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));
return factionOwnerFound != owners.end();
}
return false;
} }
void MechanicsManager::confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) void MechanicsManager::confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count)
@ -1052,6 +1064,13 @@ namespace MWMechanics
owner.first = victim.getCellRef().getRefId(); owner.first = victim.getCellRef().getRefId();
owner.second = false; owner.second = false;
const std::string victimFaction = victim.getClass().getPrimaryFaction(victim);
if (!victimFaction.empty() && Misc::StringUtils::ciEqual(item.getCellRef().getFaction(), victimFaction)) // Is the item faction-owned?
{
owner.first = victimFaction;
owner.second = true;
}
Misc::StringUtils::lowerCaseInPlace(owner.first); Misc::StringUtils::lowerCaseInPlace(owner.first);
// decrease count of stolen items // decrease count of stolen items
@ -1240,16 +1259,41 @@ namespace MWMechanics
reportCrime(player, victim, type, arg); reportCrime(player, victim, type, arg);
else if (type == OT_Assault && !victim.isEmpty()) else if (type == OT_Assault && !victim.isEmpty())
{ {
bool reported = false;
if (victim.getClass().isClass(victim, "guard") if (victim.getClass().isClass(victim, "guard")
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
reportCrime(player, victim, type, arg); reported = reportCrime(player, victim, type, arg);
else
if (!reported)
startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee? startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
} }
return crimeSeen; return crimeSeen;
} }
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) bool MechanicsManager::canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers)
{
if (actor == getPlayer()
|| !actor.getClass().isNpc() || actor.getClass().getCreatureStats(actor).isDead())
return false;
if (actor.getClass().getCreatureStats(actor).getAiSequence().isInCombat(victim))
return false;
// Unconsious actor can not report about crime and should not become hostile
if (actor.getClass().getCreatureStats(actor).getKnockedDown())
return false;
// Player's followers should not attack player, or try to arrest him
if (actor.getClass().getCreatureStats(actor).getAiSequence().hasPackage(AiPackage::TypeIdFollow))
{
if (playerFollowers.find(actor) != playerFollowers.end())
return false;
}
return true;
}
bool MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
{ {
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -1324,29 +1368,15 @@ namespace MWMechanics
bool reported = false; bool reported = false;
std::set<MWWorld::Ptr> playerFollowers;
getActorsSidingWith(player, playerFollowers);
// Tell everyone (including the original reporter) in alarm range // Tell everyone (including the original reporter) in alarm range
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 (!canReportCrime(*it, victim, playerFollowers))
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue; continue;
// Unconsious actor can not report about crime and should not become hostile
if (it->getClass().getCreatureStats(*it).getKnockedDown())
continue;
// Player's followers should not attack player, or try to arrest him
if (it->getClass().getCreatureStats(*it).getAiSequence().hasPackage(AiPackage::TypeIdFollow))
{
std::set<MWWorld::Ptr> playerFollowers;
getActorsSidingWith(player, playerFollowers);
if (playerFollowers.find(*it) != playerFollowers.end())
continue;
}
// Will the witness report the crime? // Will the witness report the crime?
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
{ {
@ -1355,8 +1385,14 @@ namespace MWMechanics
if (type == OT_Trespassing) if (type == OT_Trespassing)
MWBase::Environment::get().getDialogueManager()->say(*it, "intruder"); MWBase::Environment::get().getDialogueManager()->say(*it, "intruder");
} }
}
if (it->getClass().isClass(*it, "guard")) for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
{
if (!canReportCrime(*it, victim, playerFollowers))
continue;
if (it->getClass().isClass(*it, "guard") && reported)
{ {
// Mark as Alarmed for dialogue // Mark as Alarmed for dialogue
it->getClass().getCreatureStats(*it).setAlarmed(true); it->getClass().getCreatureStats(*it).setAlarmed(true);
@ -1454,6 +1490,8 @@ namespace MWMechanics
victim.getClass().getNpcStats(victim).setCrimeId(id); victim.getClass().getNpcStats(victim).setCrimeId(id);
} }
} }
return reported;
} }
bool MechanicsManager::actorAttacked(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker) bool MechanicsManager::actorAttacked(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker)

@ -215,7 +215,7 @@ namespace MWMechanics
virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid); virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid);
/// Has the player stolen this item from the given owner? /// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid); virtual bool isItemStolenFrom(const std::string& itemid, const MWWorld::Ptr& ptr);
virtual bool isBoundItem(const MWWorld::Ptr& item); virtual bool isBoundItem(const MWWorld::Ptr& item);
@ -234,7 +234,9 @@ namespace MWMechanics
virtual bool isSneaking(const MWWorld::Ptr& ptr); virtual bool isSneaking(const MWWorld::Ptr& ptr);
private: private:
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers);
bool reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0); OffenseType type, int arg=0);

@ -656,26 +656,25 @@ namespace MWScript
Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); Interpreter::Type_Float rotation = osg::DegreesToRadians(runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
runtime.pop(); runtime.pop();
const float *objRot = ptr.getRefData().getPosition().rot; if (!ptr.getRefData().getBaseNode())
return;
float ax = objRot[0]; // We can rotate actors only around Z axis
float ay = objRot[1]; if (ptr.getClass().isActor() && (axis == "x" || axis == "y"))
float az = objRot[2]; return;
osg::Quat rot;
if (axis == "x") if (axis == "x")
{ rot = osg::Quat(rotation, -osg::X_AXIS);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az);
}
else if (axis == "y") else if (axis == "y")
{ rot = osg::Quat(rotation, -osg::Y_AXIS);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az);
}
else if (axis == "z") else if (axis == "z")
{ rot = osg::Quat(rotation, -osg::Z_AXIS);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation);
}
else else
throw std::runtime_error ("invalid rotation axis: " + axis); throw std::runtime_error ("invalid rotation axis: " + axis);
osg::Quat attitude = ptr.getRefData().getBaseNode()->getAttitude();
MWBase::Environment::get().getWorld()->rotateWorldObject(ptr, attitude * rot);
} }
}; };

@ -1467,6 +1467,15 @@ namespace MWWorld
rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust); rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust);
} }
void World::rotateWorldObject (const Ptr& ptr, osg::Quat rotate)
{
if(ptr.getRefData().getBaseNode() != 0)
{
mRendering->rotateObject(ptr, rotate);
mPhysics->updateRotation(ptr);
}
}
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos)
{ {
return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false); return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false);

@ -224,6 +224,8 @@ namespace MWWorld
void setWaterHeight(const float height) override; void setWaterHeight(const float height) override;
void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) override;
bool toggleWater() override; bool toggleWater() override;
bool toggleWorld() override; bool toggleWorld() override;

@ -22,7 +22,7 @@ General introduction to normal map conversion
This page has general information and tutorials on how normal mapping works in OpenMW and how you can make mods using the old fake normal mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most (in)famous one to give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work in OpenMW. This page has general information and tutorials on how normal mapping works in OpenMW and how you can make mods using the old fake normal mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most (in)famous one to give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work in OpenMW.
*Note:* The conversion made in the `Converting Apel's Various Things - Sacks`_-part of this tutorial require the use of the application NifSkope. There are binaries available for Windows, but not for Mac or Linux. Reports say that NifSkope versions 1.X will compile on Linux as long as you have Qt packages installed, while the later 2.X versions will not compile. *Note:* The conversion made in the `Converting Apel's Various Things - Sacks`_-part of this tutorial require the use of the application NifSkope_.
*Another note:* I will use the terms bump mapping and normal mapping simultaneously. Normal mapping is one form of bump mapping. In other words, normal mapping is bump mapping, but bump mapping isn't necessarily normal mapping. There are several techniques for bump mapping, and normal mapping is the most common one today. *Another note:* I will use the terms bump mapping and normal mapping simultaneously. Normal mapping is one form of bump mapping. In other words, normal mapping is bump mapping, but bump mapping isn't necessarily normal mapping. There are several techniques for bump mapping, and normal mapping is the most common one today.
@ -160,8 +160,6 @@ Converting Apel's Various Things - Sacks
In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) files so that the normal maps could be loaded in Morrowind with MCP. We ignored those model files since they are not needed with OpenMW. In this tutorial however, we will convert a mod that includes new, custom made models. In other words, we cannot just ignore those files this time. In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) files so that the normal maps could be loaded in Morrowind with MCP. We ignored those model files since they are not needed with OpenMW. In this tutorial however, we will convert a mod that includes new, custom made models. In other words, we cannot just ignore those files this time.
Before we begin, you need to know that unless you want to build the NifSkope application from source yourself, you will be needing a Windows OS to do this part, since the application only has binaries available for Windows.
Tutorial - MCP, Part 2 Tutorial - MCP, Part 2
********************** **********************
@ -196,7 +194,7 @@ Since these models have one or two textures applied to them, the fix was not tha
.. _`Multiple data folders`: https://wiki.openmw.org/index.php?title=Mod_installation .. _`Multiple data folders`: https://wiki.openmw.org/index.php?title=Mod_installation
.. _`Various Things - Sacks`: https://www.nexusmods.com/morrowind/mods/42558/? .. _`Various Things - Sacks`: https://www.nexusmods.com/morrowind/mods/42558/?
.. _Lead: https://imgur.com/bwpcYlc .. _Lead: https://imgur.com/bwpcYlc
.. _NifSkope: http://niftools.sourceforge.net/wiki/NifSkope .. _NifSkope: https://wiki.openmw.org/index.php?title=Tools#NifSkope
.. _Blocks: https://imgur.com/VmQC0WG .. _Blocks: https://imgur.com/VmQC0WG
.. _`no longer have shiny models`: https://imgur.com/vu1k7n1 .. _`no longer have shiny models`: https://imgur.com/vu1k7n1
.. _`we are done`: https://imgur.com/yyZxlTw .. _`we are done`: https://imgur.com/yyZxlTw

@ -58,7 +58,6 @@ Special thanks (in alphabetical order)
Jeremiah Jeremiah
Lewis Sadlier Lewis Sadlier
Luc Keating Luc Keating
Malseph
Michael Zagar (Zoops) Michael Zagar (Zoops)
Olaxan Olaxan
psi29a psi29a

Loading…
Cancel
Save