Stolen item tracking overhaul part 2 (Fixes #2338)

celladd
scrawl 10 years ago
parent c1862cbfc2
commit bea88c3643

@ -138,9 +138,6 @@ namespace MWBase
/// @return was it illegal, and someone saw you doing it? /// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;
/// @return is \a ptr allowed to take/use \a item or is it a crime?
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) = 0;
enum PersuasionType enum PersuasionType
{ {
PT_Admire, PT_Admire,
@ -205,6 +202,15 @@ namespace MWBase
virtual void keepPlayerAlive() = 0; virtual void keepPlayerAlive() = 0;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;
/// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).
/// <Owner, item count>
virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) = 0;
/// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
}; };
} }

@ -59,8 +59,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>(); ptr.get<ESM::Container>();
// setting ownership not needed, since taking items from a container inherits the
// container's owner automatically
data->mContainerStore.fill( data->mContainerStore.fill(
ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore()); ref->mBase->mInventory, "");
// store // store
ptr.getRefData().setCustomData (data.release()); ptr.getRefData().setCustomData (data.release());
@ -82,7 +84,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>(); MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank());
// setting ownership not needed, since taking items from a container inherits the
// container's owner automatically
store.restock(list, ptr, "");
} }
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const

@ -139,8 +139,7 @@ namespace MWClass
// store // store
ptr.getRefData().setCustomData(data.release()); ptr.getRefData().setCustomData(data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1, getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr));
MWBase::Environment::get().getWorld()->getStore());
if (ref->mBase->mFlags & ESM::Creature::Weapon) if (ref->mBase->mFlags & ESM::Creature::Weapon)
getInventoryStore(ptr).autoEquip(ptr); getInventoryStore(ptr).autoEquip(ptr);
@ -886,7 +885,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); store.restock(list, ptr, ptr.getCellRef().getRefId());
} }
int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const

@ -384,8 +384,8 @@ namespace MWClass
} }
// inventory // inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1, // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
MWBase::Environment::get().getWorld()->getStore()); data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr));
data->mNpcStats.setGoldPool(gold); data->mNpcStats.setGoldPool(gold);
@ -1328,7 +1328,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); store.restock(list, ptr, ptr.getCellRef().getRefId());
} }
int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const

@ -339,7 +339,8 @@ 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 (Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), mPtr.getCellRef().getRefId())) if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(),
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)

@ -65,16 +65,7 @@ MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, I
if (item.mFlags & ItemStack::Flag_Bound) if (item.mFlags & ItemStack::Flag_Bound)
return MWWorld::Ptr(); return MWWorld::Ptr();
bool setNewOwner = false; MWWorld::Ptr ret = otherModel->copyItem(item, count, false);
// Are you dead? Then you wont need that anymore
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()
// Make sure that the item is actually owned by the dead actor
// Prevents a potential exploit for resetting the owner of any item, by placing the item in a corpse
&& Misc::StringUtils::ciEqual(item.mBase.getCellRef().getOwner(), mActor.getCellRef().getRefId()))
setNewOwner = true;
MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner);
removeItem(item, count); removeItem(item, count);
return ret; return ret;
} }

@ -46,20 +46,27 @@ namespace MWGui
ItemModel(); ItemModel();
virtual ~ItemModel() {} virtual ~ItemModel() {}
typedef int ModelIndex; typedef int ModelIndex; // -1 means invalid index
/// Throws for invalid index or out of range index
virtual ItemStack getItem (ModelIndex index) = 0; virtual ItemStack getItem (ModelIndex index) = 0;
///< throws for invalid index
/// The number of items in the model, this specifies the range of indices you can pass to
/// the getItem function (but this range is only valid until the next call to update())
virtual size_t getItemCount() = 0; virtual size_t getItemCount() = 0;
/// Returns an invalid index if the item was not found
virtual ModelIndex getIndex (ItemStack item) = 0; virtual ModelIndex getIndex (ItemStack item) = 0;
/// Rebuild the item model, this will invalidate existing model indices
virtual void update() = 0; virtual void update() = 0;
/// Move items from this model to \a otherModel. /// Move items from this model to \a otherModel.
/// @note Derived implementations may return an empty Ptr if the move was unsuccessful.
virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
/// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner? /// @param setNewOwner If true, set the copied item's owner to the actor we are copying to,
/// otherwise reset owner to ""
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0;

@ -13,6 +13,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -586,6 +587,15 @@ namespace MWGui
ret += getMiscString(cellref.getFaction(), "Faction"); ret += getMiscString(cellref.getFaction(), "Faction");
if (cellref.getFactionRank() > 0) if (cellref.getFactionRank() > 0)
ret += getValueString(cellref.getFactionRank(), "Rank"); ret += getValueString(cellref.getFactionRank(), "Rank");
std::vector<std::pair<std::string, int> > itemOwners =
MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId());
for (std::vector<std::pair<std::string, int> >::const_iterator it = itemOwners.begin(); it != itemOwners.end(); ++it)
{
ret += std::string("\nStolen ") + MyGUI::utility::toString(it->second) + " from " + it->first;
}
ret += getMiscString(cellref.getGlobalVariable(), "Global"); ret += getMiscString(cellref.getGlobalVariable(), "Global");
return ret; return ret;
} }

@ -135,11 +135,9 @@ namespace MWGui
if (i == sourceModel->getItemCount()) if (i == sourceModel->getItemCount())
throw std::runtime_error("The borrowed item disappeared"); throw std::runtime_error("The borrowed item disappeared");
// reset owner while copying, but only for items bought by the player
bool setNewOwner = (mMerchant.isEmpty());
const ItemStack& item = sourceModel->getItem(i); const ItemStack& item = sourceModel->getItem(i);
// copy the borrowed items to our model // copy the borrowed items to our model
copyItem(item, it->mCount, setNewOwner); copyItem(item, it->mCount);
// then remove them from the source model // then remove them from the source model
sourceModel->removeItem(item, it->mCount); sourceModel->removeItem(item, it->mCount);
} }

@ -300,7 +300,8 @@ 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 (Misc::StringUtils::ciEqual(it->mBase.getCellRef().getOwner(), mPtr.getCellRef().getRefId())) if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(),
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)
@ -315,6 +316,8 @@ namespace MWGui
} }
} }
// TODO: move to mwmechanics
// Is the player buying? // Is the player buying?
bool buying = (mCurrentMerchantOffer < 0); bool buying = (mCurrentMerchantOffer < 0);

@ -2,6 +2,8 @@
#include "mechanicsmanagerimp.hpp" #include "mechanicsmanagerimp.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include <components/esm/stolenitems.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -873,31 +875,31 @@ namespace MWMechanics
mAI = true; mAI = true;
} }
bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim)
{ {
const std::string& owner = item.getCellRef().getOwner(); const std::string& owner = cellref.getOwner();
bool isOwned = !owner.empty() && owner != "player"; bool isOwned = !owner.empty() && owner != "player";
const std::string& faction = item.getCellRef().getFaction(); const std::string& faction = cellref.getFaction();
bool isFactionOwned = false; bool isFactionOwned = false;
if (!faction.empty() && ptr.getClass().isNpc()) if (!faction.empty() && ptr.getClass().isNpc())
{ {
const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();
std::map<std::string, int>::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction)); std::map<std::string, int>::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction));
if (found == factions.end() if (found == factions.end()
|| found->second < item.getCellRef().getFactionRank()) || found->second < cellref.getFactionRank())
isFactionOwned = true; isFactionOwned = true;
} }
const std::string& globalVariable = item.getCellRef().getGlobalVariable(); const std::string& globalVariable = cellref.getGlobalVariable();
if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1) if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1)
{ {
isOwned = false; isOwned = false;
isFactionOwned = false; isFactionOwned = false;
} }
if (!item.getCellRef().getOwner().empty()) if (!cellref.getOwner().empty())
victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().getOwner(), true); victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true);
return (!isOwned && !isFactionOwned); return (!isOwned && !isFactionOwned);
} }
@ -916,7 +918,7 @@ namespace MWMechanics
} }
MWWorld::Ptr victim; MWWorld::Ptr victim;
if (isAllowedToUse(ptr, bed, victim)) if (isAllowedToUse(ptr, bed.getCellRef(), victim))
return false; return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
@ -931,27 +933,82 @@ namespace MWMechanics
void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
{ {
MWWorld::Ptr victim; MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item, victim)) if (isAllowedToUse(ptr, item.getCellRef(), victim))
return; return;
commitCrime(ptr, victim, OT_Trespassing); commitCrime(ptr, victim, OT_Trespassing);
} }
std::vector<std::pair<std::string, int> > MechanicsManager::getStolenItemOwners(const std::string& itemid)
{
std::vector<std::pair<std::string, int> > result;
StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid));
if (it == mStolenItems.end())
return result;
else
{
const OwnerMap& owners = it->second;
for (OwnerMap::const_iterator ownerIt = owners.begin(); ownerIt != owners.end(); ++ownerIt)
result.push_back(std::make_pair(ownerIt->first.first, ownerIt->second));
return result;
}
}
bool MechanicsManager::isItemStolenFrom(const std::string &itemid, const std::string &ownerid)
{
StolenItemsMap::const_iterator it = mStolenItems.find(Misc::StringUtils::lowerCase(itemid));
if (it == mStolenItems.end())
return false;
const OwnerMap& owners = it->second;
OwnerMap::const_iterator ownerFound = owners.find(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));
return ownerFound != owners.end();
}
void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
{
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
StolenItemsMap::iterator stolenIt = mStolenItems.find(Misc::StringUtils::lowerCase(it->getClass().getId(*it)));
if (stolenIt == mStolenItems.end())
continue;
OwnerMap& owners = stolenIt->second;
int itemCount = it->getRefData().getCount();
for (OwnerMap::iterator ownerIt = owners.begin(); ownerIt != owners.end();)
{
int toRemove = std::min(itemCount, ownerIt->second);
itemCount -= toRemove;
ownerIt->second -= toRemove;
if (ownerIt->second == 0)
owners.erase(ownerIt++);
else
++ownerIt;
}
int toMove = it->getRefData().getCount() - itemCount;
targetContainer.getClass().getContainerStore(targetContainer).add(*it, toMove, targetContainer);
store.remove(*it, toMove, player);
}
// TODO: unhardcode the locklevel
targetContainer.getClass().lock(targetContainer,50);
}
void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container,
int count) int count)
{ {
if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr())
return;
MWWorld::Ptr victim; MWWorld::Ptr victim;
const MWWorld::CellRef* ownerCellRef = &item.getCellRef();
if (!container.isEmpty()) if (!container.isEmpty())
{ {
// Inherit the owner of the container // Inherit the owner of the container
if (isAllowedToUse(ptr, container, victim)) ownerCellRef = &container.getCellRef();
return;
} }
else else
{ {
if (isAllowedToUse(ptr, item, victim))
return;
if (!item.getCellRef().hasContentFile()) if (!item.getCellRef().hasContentFile())
{ {
// this is a manually placed item, which means it was already stolen // this is a manually placed item, which means it was already stolen
@ -959,6 +1016,20 @@ namespace MWMechanics
} }
} }
if (isAllowedToUse(ptr, *ownerCellRef, victim))
return;
Owner owner;
owner.first = ownerCellRef->getOwner();
owner.second = false;
if (owner.first.empty())
{
owner.first = ownerCellRef->getFaction();
owner.second = true;
}
Misc::StringUtils::toLower(owner.first);
mStolenItems[Misc::StringUtils::lowerCase(item.getClass().getId(item))][owner] += count;
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count);
} }
@ -967,7 +1038,7 @@ namespace MWMechanics
// NOTE: victim may be empty // NOTE: victim may be empty
// Only player can commit crime // Only player can commit crime
if (player.getRefData().getHandle() != "player") if (player != MWBase::Environment::get().getWorld()->getPlayerPtr())
return false; return false;
// Find all the actors within the alarm radius // Find all the actors within the alarm radius
@ -1369,22 +1440,37 @@ namespace MWMechanics
int MechanicsManager::countSavedGameRecords() const int MechanicsManager::countSavedGameRecords() const
{ {
return 1; // Death counter return 1 // Death counter
+1; // Stolen items
} }
void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const
{ {
mActors.write(writer, listener); mActors.write(writer, listener);
ESM::StolenItems items;
items.mStolenItems = mStolenItems;
writer.startRecord(ESM::REC_STLN);
items.write(writer);
writer.endRecord(ESM::REC_STLN);
} }
void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type) void MechanicsManager::readRecord(ESM::ESMReader &reader, uint32_t type)
{ {
if (type == ESM::REC_STLN)
{
ESM::StolenItems items;
items.load(reader);
mStolenItems = items.mStolenItems;
}
else
mActors.readRecord(reader, type); mActors.readRecord(reader, type);
} }
void MechanicsManager::clear() void MechanicsManager::clear()
{ {
mActors.clear(); mActors.clear();
mStolenItems.clear();
} }
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)

@ -35,6 +35,11 @@ namespace MWMechanics
Objects mObjects; Objects mObjects;
Actors mActors; Actors mActors;
typedef std::pair<std::string, bool> Owner; // < Owner id, bool isFaction >
typedef std::map<Owner, int> OwnerMap; // < Owner, number of stolen items with this id from this owner >
typedef std::map<std::string, OwnerMap> StolenItemsMap;
StolenItemsMap mStolenItems;
public: public:
void buildPlayer(); void buildPlayer();
@ -130,9 +135,6 @@ namespace MWMechanics
/// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby /// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed);
/// @return is \a ptr allowed to take/use \a item or is it a crime?
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim);
virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
@ -170,9 +172,21 @@ namespace MWMechanics
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer);
/// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).
/// <Owner, item count>
virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid);
/// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid);
private: private:
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0); OffenseType type, int arg=0);
/// @return is \a ptr allowed to take/use \a cellref or is it a crime?
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim);
}; };
} }

@ -421,6 +421,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
break; break;
case ESM::REC_DCOU: case ESM::REC_DCOU:
case ESM::REC_STLN:
MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.val); MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.val);
break; break;

@ -117,6 +117,15 @@ namespace MWWorld
return mCellRef.mGlobalVariable; return mCellRef.mGlobalVariable;
} }
void CellRef::resetGlobalVariable()
{
if (!mCellRef.mGlobalVariable.empty())
{
mChanged = true;
mCellRef.mGlobalVariable.erase();
}
}
void CellRef::setFactionRank(int factionRank) void CellRef::setFactionRank(int factionRank)
{ {
if (factionRank != mCellRef.mFactionRank) if (factionRank != mCellRef.mFactionRank)

@ -75,6 +75,8 @@ namespace MWWorld
// Used by bed rent scripts to allow the player to use the bed for the duration of the rent. // Used by bed rent scripts to allow the player to use the bed for the duration of the rent.
std::string getGlobalVariable() const; std::string getGlobalVariable() const;
void resetGlobalVariable();
// ID of creature trapped in this soul gem // ID of creature trapped in this soul gem
std::string getSoul() const; std::string getSoul() const;
void setSoul(const std::string& soul); void setSoul(const std::string& soul);

@ -80,6 +80,8 @@ namespace MWWorld
virtual std::string getId (const Ptr& ptr) const; virtual std::string getId (const Ptr& ptr) const;
///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval ///< Return ID of \a ptr or throw an exception, if class does not support ID retrieval
/// (default implementation: throw an exception) /// (default implementation: throw an exception)
/// @note This function is currently redundant; the same ID can be retrieved by CellRef::getRefId.
/// Leaving it here for now in case we want to optimize later.
virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const;
virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWWorld::PhysicsSystem& physics) const; virtual void insertObject(const Ptr& ptr, const std::string& mesh, MWWorld::PhysicsSystem& physics) const;

@ -222,28 +222,21 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
MWWorld::ContainerStoreIterator it = end(); MWWorld::ContainerStoreIterator it = end();
if (setOwner && actorPtr.getClass().isActor())
{
// HACK: Set owner on the original item, then reset it after we have copied it // HACK: Set owner on the original item, then reset it after we have copied it
// If we set the owner on the copied item, it would not stack correctly... // If we set the owner on the copied item, it would not stack correctly...
std::string oldOwner = itemPtr.getCellRef().getOwner(); std::string oldOwner = itemPtr.getCellRef().getOwner();
if (actorPtr == player) if (!setOwner || actorPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) // No point in setting owner to the player - NPCs will not respect this anyway
{ {
// No point in setting owner to the player - NPCs will not respect this anyway
// Additionally, setting it to "player" would make those items not stack with items that don't have an owner
itemPtr.getCellRef().setOwner(""); itemPtr.getCellRef().setOwner("");
} }
else else
{
itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId()); itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId());
}
it = addImp(itemPtr, count); it = addImp(itemPtr, count);
itemPtr.getCellRef().setOwner(oldOwner); itemPtr.getCellRef().setOwner(oldOwner);
}
else
{
it = addImp(itemPtr, count);
}
// The copy of the original item we just made // The copy of the original item we just made
MWWorld::Ptr item = *it; MWWorld::Ptr item = *it;
@ -258,6 +251,14 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
pos.pos[1] = 0; pos.pos[1] = 0;
pos.pos[2] = 0; pos.pos[2] = 0;
item.getCellRef().setPosition(pos); item.getCellRef().setPosition(pos);
// reset ownership stuff, owner was already handled above
item.getCellRef().resetGlobalVariable();
item.getCellRef().setFaction("");
item.getCellRef().setFactionRank(-1);
// must reset the RefNum on the copied item, so that the RefNum on the original item stays unique
// maybe we should do this in the copy constructor instead?
item.getCellRef().unsetRefNum(); // destroy link to content file item.getCellRef().unsetRefNum(); // destroy link to content file
std::string script = item.getClass().getScript(item); std::string script = item.getClass().getScript(item);
@ -399,19 +400,19 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
return count - toRemove; return count - toRemove;
} }
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store) void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner)
{ {
for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); for (std::vector<ESM::ContItem>::const_iterator iter (items.mList.begin()); iter!=items.mList.end();
++iter) ++iter)
{ {
std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString()); std::string id = Misc::StringUtils::lowerCase(iter->mItem.toString());
addInitialItem(id, owner, faction, factionRank, iter->mCount); addInitialItem(id, owner, iter->mCount);
} }
flagAsModified(); flagAsModified();
} }
void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner,
int count, bool topLevel, const std::string& levItem) int count, bool topLevel, const std::string& levItem)
{ {
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
@ -423,7 +424,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each) if (topLevel && std::abs(count) > 1 && levItem->mFlags & ESM::ItemLevList::Each)
{ {
for (int i=0; i<std::abs(count); ++i) for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, faction, factionRank, count > 0 ? 1 : -1, true, levItem->mId); addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItem->mId);
return; return;
} }
else else
@ -431,7 +432,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false); std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (id.empty()) if (id.empty())
return; return;
addInitialItem(id, owner, faction, factionRank, count, false, levItem->mId); addInitialItem(id, owner, count, false, levItem->mId);
} }
} }
else else
@ -447,13 +448,11 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
count = std::abs(count); count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner); ref.getPtr().getCellRef().setOwner(owner);
ref.getPtr().getCellRef().setFaction(faction);
ref.getPtr().getCellRef().setFactionRank(factionRank);
addImp (ref.getPtr(), count); addImp (ref.getPtr(), count);
} }
} }
void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank) void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)
{ {
// Remove the items already spawned by levelled items that will restock // Remove the items already spawned by levelled items that will restock
for (std::map<std::string, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) for (std::map<std::string, int>::iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it)
@ -472,13 +471,13 @@ void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MW
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem.toString())) if (MWBase::Environment::get().getWorld()->getStore().get<ESM::ItemLevList>().search(it->mItem.toString()))
{ {
addInitialItem(item, owner, faction, factionRank, it->mCount, true); addInitialItem(item, owner, it->mCount, true);
} }
else else
{ {
int currentCount = count(item); int currentCount = count(item);
if (currentCount < std::abs(it->mCount)) if (currentCount < std::abs(it->mCount))
addInitialItem(item, owner, faction, factionRank, std::abs(it->mCount) - currentCount, true); addInitialItem(item, owner, std::abs(it->mCount) - currentCount, true);
} }
} }
flagAsModified(); flagAsModified();

@ -75,7 +75,7 @@ namespace MWWorld
mutable float mCachedWeight; mutable float mCachedWeight;
mutable bool mWeightUpToDate; mutable bool mWeightUpToDate;
ContainerStoreIterator addImp (const Ptr& ptr, int count); ContainerStoreIterator addImp (const Ptr& ptr, int count);
void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int factionRank, int count, bool topLevel=true, const std::string& levItem = ""); void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
template<typename T> template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection, ContainerStoreIterator getState (CellRefList<T>& collection,
@ -112,7 +112,7 @@ namespace MWWorld
/// \attention Do not add items to an existing stack by increasing the count instead of /// \attention Do not add items to an existing stack by increasing the count instead of
/// calling this function! /// calling this function!
/// ///
/// @param setOwner Set the owner of the added item to \a actorPtr? /// @param setOwner Set the owner of the added item to \a actorPtr? If false, the owner is reset to "".
/// ///
/// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item.
@ -151,10 +151,10 @@ namespace MWWorld
virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2);
///< @return true if the two specified objects can stack with each other ///< @return true if the two specified objects can stack with each other
void fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, int factionRank, const MWWorld::ESMStore& store); void fill (const ESM::InventoryList& items, const std::string& owner);
///< Insert items into *this. ///< Insert items into *this.
void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner, const std::string& faction, int factionRank); void restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner);
virtual void clear(); virtual void clear();
///< Empty container. ///< Empty container.

@ -2980,18 +2980,10 @@ namespace MWWorld
if (!closestChest.isEmpty()) //Found a close chest if (!closestChest.isEmpty()) //Found a close chest
{ {
ContainerStore& store = ptr.getClass().getContainerStore(ptr); MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) //Move all stolen stuff into chest
{
MWWorld::Ptr dummy;
if (!MWBase::Environment::get().getMechanicsManager()->isAllowedToUse(getPlayerPtr(), *it, dummy))
{
closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest);
store.remove(*it, it->getRefData().getCount(), ptr);
}
}
closestChest.getClass().lock(closestChest,50);
} }
else
std::cerr << "Failed to confiscate items: no stolen_goods container found" << std::endl;
} }
void World::goToJail() void World::goToJail()

@ -63,7 +63,7 @@ add_component_dir (esm
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects util custommarkerstate aisequence magiceffects util custommarkerstate stolenitems
) )
add_component_dir (esmterrain add_component_dir (esmterrain

@ -117,6 +117,7 @@ enum RecNameInts
REC_MARK = FourCC<'M','A','R','K'>::value, REC_MARK = FourCC<'M','A','R','K'>::value,
REC_ENAB = FourCC<'E','N','A','B'>::value, REC_ENAB = FourCC<'E','N','A','B'>::value,
REC_CAM_ = FourCC<'C','A','M','_'>::value, REC_CAM_ = FourCC<'C','A','M','_'>::value,
REC_STLN = FourCC<'S','T','L','N'>::value,
// format 1 // format 1
REC_FILT = FourCC<'F','I','L','T'>::value, REC_FILT = FourCC<'F','I','L','T'>::value,

@ -0,0 +1,47 @@
#include "stolenitems.hpp"
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
namespace ESM
{
void StolenItems::write(ESMWriter &esm) const
{
for (StolenItemsMap::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
{
esm.writeHNString("NAME", it->first);
for (std::map<std::pair<std::string, bool>, int>::const_iterator ownerIt = it->second.begin();
ownerIt != it->second.end(); ++ownerIt)
{
if (ownerIt->first.second)
esm.writeHNString("FNAM", ownerIt->first.first);
else
esm.writeHNString("ONAM", ownerIt->first.first);
esm.writeHNT("COUN", ownerIt->second);
}
}
}
void StolenItems::load(ESMReader &esm)
{
while (esm.isNextSub("NAME"))
{
std::string itemid = esm.getHString();
std::map<std::pair<std::string, bool>, int> ownerMap;
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
{
std::string subname = esm.retSubName().toString();
std::string owner = esm.getHString();
bool isFaction = (subname == "FNAM");
int count;
esm.getHNT(count, "COUN");
ownerMap.insert(std::make_pair(std::make_pair(owner, isFaction), count));
}
mStolenItems[itemid] = ownerMap;
}
}
}

@ -0,0 +1,24 @@
#ifndef OPENMW_COMPONENTS_ESM_STOLENITEMS_H
#define OPENMW_COMPONENTS_ESM_STOLENITEMS_H
#include <map>
#include <string>
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct StolenItems
{
typedef std::map<std::string, std::map<std::pair<std::string, bool>, int> > StolenItemsMap;
StolenItemsMap mStolenItems;
void load(ESM::ESMReader& esm);
void write(ESM::ESMWriter& esm) const;
};
}
#endif
Loading…
Cancel
Save