Native graphics herbalism support (feature #5010)

pull/541/head
Andrei Kortunov 6 years ago
parent 0bec84342c
commit 861d41f4a4

@ -105,6 +105,7 @@
Feature #4968: Scalable UI widget skins
Feature #4994: Persistent pinnable windows hiding
Feature #5000: Compressed BSA format support
Feature #5010: Native graphics herbalism support
Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption
Task #4721: Add NMake support to the Windows prebuild script

@ -62,7 +62,7 @@ add_openmw_dir (mwsound
add_openmw_dir (mwworld
refdata worldimp scene globals class action nullaction actionteleport
containerstore actiontalk actiontake manualref player cellvisitors failedaction
cells localscripts customdata inventorystore ptr actionopen actionread
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager

@ -578,6 +578,7 @@ namespace MWBase
/// Return the distance between actor's weapon and target's collision box.
virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
virtual void addContainerScripts(const MWWorld::Ptr& reference, MWWorld::CellStore* cell) = 0;
virtual void removeContainerScripts(const MWWorld::Ptr& reference) = 0;
virtual bool isPlayerInJail() const = 0;

@ -15,6 +15,7 @@
#include "../mwworld/customdata.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/actionharvest.hpp"
#include "../mwworld/actionopen.hpp"
#include "../mwworld/actiontrap.hpp"
#include "../mwphysics/physicssystem.hpp"
@ -22,6 +23,7 @@
#include "../mwgui/tooltips.hpp"
#include "../mwrender/animation.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
@ -40,6 +42,10 @@ namespace MWClass
{
return *this;
}
virtual const ContainerCustomData& asContainerCustomData() const
{
return *this;
}
};
MWWorld::CustomData *ContainerCustomData::clone() const
@ -63,9 +69,20 @@ namespace MWClass
// store
ptr.getRefData().setCustomData (data.release());
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
}
}
bool canBeHarvested(const MWWorld::ConstPtr& ptr)
{
const MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if (animation == nullptr)
return false;
return animation->canBeHarvested();
}
void Container::respawn(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Container> *ref =
@ -175,6 +192,12 @@ namespace MWClass
{
if(!isTrapped)
{
if (canBeHarvested(ptr))
{
std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionHarvest(ptr));
return action;
}
std::shared_ptr<MWWorld::Action> action (new MWWorld::ActionOpen(ptr));
return action;
}
@ -225,9 +248,18 @@ namespace MWClass
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
if (getName(ptr).empty())
return false;
return (ref->mBase->mName != "");
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
return true;
}
bool Container::canBeActivated(const MWWorld::Ptr& ptr) const
{
return hasToolTip(ptr);
}
MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const

@ -63,6 +63,8 @@ namespace MWClass
const;
///< Write additional state from \a ptr into \a state.
virtual bool canBeActivated(const MWWorld::Ptr& ptr) const;
static void registerSelf();
virtual void respawn (const MWWorld::Ptr& ptr) const;

@ -949,6 +949,9 @@ namespace MWMechanics
return true;
}
if (!target.getClass().canBeActivated(target))
return true;
// TODO: implement a better check to check if target is owned bed
if (target.getClass().isActivator() && target.getClass().getScript(target).compare(0, 3, "Bed") != 0)
return true;

@ -131,6 +131,25 @@ namespace
}
};
class HarvestVisitor : public osg::NodeVisitor
{
public:
HarvestVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
virtual void apply(osg::Switch& node)
{
if (node.getName() == Constants::HerbalismLabel)
{
node.setSingleChildOn(1);
}
traverse(node);
}
};
NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname)
{
NifOsg::TextKeyMap::const_iterator iter(keys.begin());
@ -1970,6 +1989,30 @@ namespace MWRender
AddSwitchCallbacksVisitor visitor;
mObjectRoot->accept(visitor);
}
if (ptr.getTypeName() == typeid(ESM::Container).name() &&
SceneUtil::hasUserDescription(mObjectRoot, Constants::HerbalismLabel) &&
ptr.getRefData().getCustomData() != nullptr)
{
const MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
if (!store.hasVisibleItems())
{
HarvestVisitor visitor;
mObjectRoot->accept(visitor);
}
}
}
bool ObjectAnimation::canBeHarvested() const
{
if (mPtr.getTypeName() != typeid(ESM::Container).name())
return false;
const MWWorld::LiveCellRef<ESM::Container>* ref = mPtr.get<ESM::Container>();
if (!(ref->mBase->mFlags & ESM::Container::Organic))
return false;
return SceneUtil::hasUserDescription(mObjectRoot, Constants::HerbalismLabel);
}
Animation::AnimState::~AnimState()

@ -475,6 +475,7 @@ public:
virtual float getHeadPitch() const;
virtual float getHeadYaw() const;
virtual void setAccurateAiming(bool enabled) {}
virtual bool canBeHarvested() const { return false; }
private:
Animation(const Animation&);
@ -484,6 +485,8 @@ private:
class ObjectAnimation : public Animation {
public:
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight);
bool canBeHarvested() const;
};
class UpdateVfxCallback : public osg::NodeCallback

@ -0,0 +1,93 @@
#include "actionharvest.hpp"
#include <sstream>
#include <MyGUI_LanguageManager.h>
#include <components/misc/stringops.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "class.hpp"
#include "containerstore.hpp"
namespace MWWorld
{
ActionHarvest::ActionHarvest (const MWWorld::Ptr& container)
: Action (true, container)
{
setSound("Item Ingredient Up");
}
void ActionHarvest::executeImp (const MWWorld::Ptr& actor)
{
if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
return;
MWWorld::Ptr target = getTarget();
MWWorld::ContainerStore& store = target.getClass().getContainerStore (target);
MWWorld::ContainerStore& actorStore = actor.getClass().getContainerStore(actor);
std::map<std::string, int> takenMap;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (!it->getClass().showsInInventory(*it))
continue;
int itemCount = it->getRefData().getCount();
// Note: it is important to check for crime before move an item from container. Otherwise owner check will not work
// for a last item in the container - empty harvested containers are considered as "allowed to use".
MWBase::Environment::get().getMechanicsManager()->itemTaken(actor, *it, target, itemCount);
actorStore.add(*it, itemCount, actor);
store.remove(*it, itemCount, getTarget());
takenMap[it->getClass().getName(*it)]+=itemCount;
}
// Spawn a messagebox (only for items added to player's inventory)
if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
std::ostringstream stream;
int lineCount = 0;
const static int maxLines = 10;
for (auto & pair : takenMap)
{
std::string itemName = pair.first;
int itemCount = pair.second;
lineCount++;
if (lineCount == maxLines)
stream << "\n...";
else if (lineCount > maxLines)
break;
// The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory
std::string msgBox;
if (itemCount == 1)
{
msgBox = MyGUI::LanguageManager::getInstance().replaceTags("\n#{sNotifyMessage60}");
Misc::StringUtils::replace(msgBox, "%s", itemName.c_str(), 2);
}
else
{
msgBox = MyGUI::LanguageManager::getInstance().replaceTags("\n#{sNotifyMessage61}");
Misc::StringUtils::replace(msgBox, "%d", std::to_string(itemCount).c_str(), 2);
Misc::StringUtils::replace(msgBox, "%s", itemName.c_str(), 2);
}
stream << msgBox;
}
std::string tooltip = stream.str();
// remove the first newline (easier this way)
if (tooltip.size() > 0 && tooltip[0] == '\n')
tooltip.erase(0, 1);
if (tooltip.size() > 0)
MWBase::Environment::get().getWindowManager()->messageBox(tooltip);
}
// Update animation object
MWBase::Environment::get().getWorld()->disable(target);
MWBase::Environment::get().getWorld()->enable(target);
}
}

@ -0,0 +1,19 @@
#ifndef GAME_MWWORLD_ACTIONHARVEST_H
#define GAME_MWWORLD_ACTIONHARVEST_H
#include "action.hpp"
#include "ptr.hpp"
namespace MWWorld
{
class ActionHarvest : public Action
{
virtual void executeImp (const MWWorld::Ptr& actor);
public:
ActionHarvest (const Ptr& container);
///< \param container The Container the Player has activated.
};
}
#endif // ACTIONOPEN_H

@ -422,6 +422,17 @@ int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const
return count - toRemove;
}
bool MWWorld::ContainerStore::hasVisibleItems() const
{
for (auto iter(begin()); iter != end(); ++iter)
{
if (iter->getClass().showsInInventory(*iter))
return true;
}
return false;
}
int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor)
{
assert(this == item.getContainerStore());

@ -128,6 +128,8 @@ namespace MWWorld
ContainerStoreIterator begin (int mask = Type_All);
ContainerStoreIterator end();
bool hasVisibleItems() const;
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
///

@ -42,6 +42,13 @@ MWClass::ContainerCustomData &CustomData::asContainerCustomData()
throw std::logic_error(error.str());
}
const MWClass::ContainerCustomData &CustomData::asContainerCustomData() const
{
std::stringstream error;
error << "bad cast " << typeid(this).name() << " to ContainerCustomData";
throw std::logic_error(error.str());
}
MWClass::DoorCustomData &CustomData::asDoorCustomData()
{
std::stringstream error;

@ -30,6 +30,7 @@ namespace MWWorld
virtual const MWClass::NpcCustomData& asNpcCustomData() const;
virtual MWClass::ContainerCustomData& asContainerCustomData();
virtual const MWClass::ContainerCustomData& asContainerCustomData() const;
virtual MWClass::DoorCustomData& asDoorCustomData();
virtual const MWClass::DoorCustomData& asDoorCustomData() const;

@ -42,6 +42,11 @@ namespace
bool operator()(const MWWorld::Ptr& containerPtr)
{
// Ignore containers without generated content
if (containerPtr.getTypeName() == typeid(ESM::Container).name() &&
containerPtr.getRefData().getCustomData() == nullptr)
return false;
MWWorld::ContainerStore& container = containerPtr.getClass().getContainerStore(containerPtr);
for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it)
{

@ -141,9 +141,9 @@ namespace MWWorld
MWWorld::Ptr getFacedObject(float maxDistance, bool ignorePlayer=true);
public: // FIXME
void addContainerScripts(const Ptr& reference, CellStore* cell) override;
void removeContainerScripts(const Ptr& reference) override;
private:
void addContainerScripts(const Ptr& reference, CellStore* cell);
void PCDropped (const Ptr& item);
void processDoors(float duration);

@ -27,6 +27,9 @@ const int CellSizeInUnits = 8192;
// A label to mark night/day visual switches
const std::string NightDayLabel = "NightDaySwitch";
// A label to mark visual switches for herbalism feature
const std::string HerbalismLabel = "HerbalismSwitch";
}
#endif

@ -635,6 +635,8 @@ namespace NifOsg
const Nif::NiSwitchNode* niSwitchNode = static_cast<const Nif::NiSwitchNode*>(nifNode);
if (niSwitchNode->name == Constants::NightDayLabel && !SceneUtil::hasUserDescription(rootNode, Constants::NightDayLabel))
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::NightDayLabel);
else if (niSwitchNode->name == Constants::HerbalismLabel && !SceneUtil::hasUserDescription(rootNode, Constants::HerbalismLabel))
rootNode->getOrCreateUserDataContainer()->addDescription(Constants::HerbalismLabel);
}
return node;

Loading…
Cancel
Save