I'm creating a MUD and am running into some design issues regarding Items.

Items are stored in an ItemDatabase after being read from a JSON file. 

    template <typename ItemType>
	static void load(std::string const& filename)
	{
		PropertyTree propertyTree;
		boost::property_tree::read_json(filename, propertyTree);
		
		for (auto& jsonItem : propertyTree)
		{
			Item *item = new ItemType();
			item->fromJson(jsonItem);

			database_[item->id()] = item;
		}
	}

At first, every Database::find() returned a shallow copy of the item, however, this quickly becomes dangerous and erroneous when items have a finite usage, e.g. a life potion that is consumed after one charge.

Therefore, I want to start returning deep copies as std::unique_ptrs because each Item is unique and is possessed by only one entity. This avoids dangerous dangling pointers, if the Item is not maintained or destroyed by the caller:

    template <typename DataType>
    std::unique_ptr<DataType> EntityDatabase<DataType>::find(EntityId const& entityId)
    {
	    auto found = std::find_if(std::begin(database_), std::end(database_), 
							      [&entityId](Container::value_type const& candidate)
							      {
								      return (candidate.second->id() == entityId);
							      });

	    if (found != std::end(database_))
		    return std::unique_ptr<DataType>(new DataType(*found->second));
	    else
		    return nullptr;
    }

However, the resulting code is worse than hideous. Consider turning an `Item` into a `Weapon`:

	void Inventory::fromJson(PropertyTree::value_type const& properties)
	{
		auto jsonInventory = *properties.second.find("Inventory");

		auto weaponId = getValue<EntityId>(jsonInventory, "Weapon");
		weapon_.reset(static_cast<Weapon*>(ItemDatabase::find(weaponId).release()));

		auto armorId  = getValue<EntityId>(jsonInventory, "Armor");
		armor_.reset(static_cast<Armor*>(ItemDatabase::find(armorId).release()));

		money_ = getValue<Money>(jsonInventory, "Money");

		for (auto& item : jsonInventory.second.get_child("Items"))
		{
			auto itemId = boost::lexical_cast<EntityId, std::string>(item.second.data());
			inventory_.push_back(std::move(ItemDatabase::find(itemId)));
		}
	}

How can I improve the code or the design?

Edit:

Some of the assumptions made by Yuushi are correct and I wish to state them here:

EntityDatabase is the base class for all Databases such as ItemDatabase. It contains raw pointers to objects created after reading the appropriate Json-File. 

	template <typename DataType>
	class EntityDatabase
	{
	public:
		typedef std::map<EntityId, DataType*> Container;

Loading happens in the derived Databases, for example the loading of Items including Armor, Weapons, Consumables:

	class ItemDatabase : public EntityDatabase<Item>
	{
	public:
		static void load();

		template <typename ItemType>
		static void load(std::string const& filename)
		{
			PropertyTree propertyTree;
			boost::property_tree::read_json(filename, propertyTree);
		
			for (auto& jsonItem : propertyTree)
			{
				Item *item = new ItemType();
				item->fromJson(jsonItem);

				database_[item->id()] = item;
			}
		}

Further, Weapon, Armor, Consumable are all of type `Item`:

	class Item
	{
		Item();
		virtual ~Item();
		//....
	}

	class Weapon : public Item
	{ 
		//...
	}

	class Armor : public Item
	{
		//...
	}

ItemDatabase::find() returns a `unique_ptr<Item>`, a deep copy of the pointer object to be owned by only one person:

	static std::unique_ptr<Item> find(EntityId const& id)
	{
		auto item = database_.find(id);
		if (item != std::end(database_))
			return std::unique_ptr<Item>(new Item(**item));
		else
			return nullptr;
	}

The Inventory class I showed whose `weapon_` and `armor_` fields must be set looks like this:

	class Inventory : public JsonObject
	{
	public:
		Inventory();
		~Inventory();

		virtual void fromJson(PropertyTree::value_type const& properties) override;
		virtual void toJson(PropertyTree& propertyTree);

		void equipWeapon(Weapon *weapon);
		void removeWeapon();
		std::shared_ptr<Weapon> weapon() const;
	
		void equipArmor(Armor *armor);
		void removeArmor();
		std::shared_ptr<Armor> armor() const;

		void pickUpItem(Item *item);
		std::shared_ptr<Item> dropItem(Item *item);
		void destroyItem(Item *item);

		std::shared_ptr<Item> getItem(Item *item);
		std::shared_ptr<Item> findItem(std::string const& name);
		bool hasItem(Item *item);

		void earnMoney(Money amount);
		void spendMoney(Money amount);
		Money money() const;

		size_t size() const;

		friend std::ostream& operator<<(std::ostream& os, Inventory const& inventory);

	private:
		bool isBeingUsedAsAWeapon(Item *item) const;
		bool isBeingUsedAsArmor(Item *item) const;

	private:
		std::shared_ptr<Weapon> weapon_;
		std::shared_ptr<Armor> armor_;
		std::vector<std::shared_ptr<Item>> inventory_;

		Money money_;
	};

Wherein an Item only of type Weapon can be equipped as a weapon, but both Weapon and Armor must be displayed and kept in the inventory.