#pragma once #include #include #include #include #include "items.h" #include "player.h" namespace devilution { /** * @brief A range over non-empty items in a container. */ class ItemsContainerRange { public: class Iterator { public: using iterator_category = std::forward_iterator_tag; using difference_type = int; using value_type = Item; using pointer = value_type *; using reference = value_type &; Iterator() = default; Iterator(Item *items, std::size_t count, std::size_t index) : items_(items) , count_(count) , index_(index) { advancePastEmpty(); } pointer operator->() const { return &items_[index_]; } reference operator*() const { return items_[index_]; } Iterator &operator++() { ++index_; advancePastEmpty(); return *this; } Iterator operator++(int) { auto copy = *this; ++(*this); return copy; } bool operator==(const Iterator &other) const { return index_ == other.index_; } bool operator!=(const Iterator &other) const { return !(*this == other); } [[nodiscard]] bool atEnd() const { return index_ == count_; } [[nodiscard]] std::size_t index() const { return index_; } private: void advancePastEmpty() { while (index_ < count_ && items_[index_].isEmpty()) { ++index_; } } Item *items_ = nullptr; std::size_t count_ = 0; std::size_t index_ = 0; }; ItemsContainerRange(Item *items, std::size_t count) : items_(items) , count_(count) { } [[nodiscard]] Iterator begin() const { return Iterator { items_, count_, 0 }; } [[nodiscard]] Iterator end() const { return Iterator { nullptr, count_, count_ }; } private: Item *items_; std::size_t count_; }; /** * @brief A range over non-empty items in a list of containers. */ class ItemsContainerListRange { public: class Iterator { public: using iterator_category = std::forward_iterator_tag; using difference_type = int; using value_type = Item; using pointer = value_type *; using reference = value_type &; Iterator() = default; explicit Iterator(std::vector iterators) : iterators_(std::move(iterators)) { advancePastEmpty(); } pointer operator->() const { return iterators_[current_].operator->(); } reference operator*() const { return iterators_[current_].operator*(); } Iterator &operator++() { ++iterators_[current_]; advancePastEmpty(); return *this; } Iterator operator++(int) { auto copy = *this; ++(*this); return copy; } bool operator==(const Iterator &other) const { return current_ == other.current_ && iterators_[current_] == other.iterators_[current_]; } bool operator!=(const Iterator &other) const { return !(*this == other); } private: void advancePastEmpty() { while (current_ + 1 < iterators_.size() && iterators_[current_].atEnd()) { ++current_; } } std::vector iterators_; std::size_t current_ = 0; }; }; /** * @brief A range over equipped player items. */ class EquippedPlayerItemsRange { public: explicit EquippedPlayerItemsRange(Player &player) : player_(&player) { } [[nodiscard]] ItemsContainerRange::Iterator begin() const { return ItemsContainerRange::Iterator { &player_->InvBody[0], containerSize(), 0 }; } [[nodiscard]] ItemsContainerRange::Iterator end() const { return ItemsContainerRange::Iterator { nullptr, containerSize(), containerSize() }; } private: [[nodiscard]] std::size_t containerSize() const { return sizeof(player_->InvBody) / sizeof(player_->InvBody[0]); } Player *player_; }; /** * @brief A range over non-equipped inventory player items. */ class InventoryPlayerItemsRange { public: explicit InventoryPlayerItemsRange(Player &player) : player_(&player) { } [[nodiscard]] ItemsContainerRange::Iterator begin() const { return ItemsContainerRange::Iterator { &player_->InvList[0], containerSize(), 0 }; } [[nodiscard]] ItemsContainerRange::Iterator end() const { return ItemsContainerRange::Iterator { nullptr, containerSize(), containerSize() }; } private: [[nodiscard]] std::size_t containerSize() const { return static_cast(player_->_pNumInv); } Player *player_; }; /** * @brief A range over belt player items. */ class BeltPlayerItemsRange { public: explicit BeltPlayerItemsRange(Player &player) : player_(&player) { } [[nodiscard]] ItemsContainerRange::Iterator begin() const { return ItemsContainerRange::Iterator { &player_->SpdList[0], containerSize(), 0 }; } [[nodiscard]] ItemsContainerRange::Iterator end() const { return ItemsContainerRange::Iterator { nullptr, containerSize(), containerSize() }; } private: [[nodiscard]] std::size_t containerSize() const { return sizeof(player_->SpdList) / sizeof(player_->SpdList[0]); } Player *player_; }; /** * @brief A range over non-equipped player items in the following order: Inventory, Belt. */ class InventoryAndBeltPlayerItemsRange { public: explicit InventoryAndBeltPlayerItemsRange(Player &player) : player_(&player) { } [[nodiscard]] ItemsContainerListRange::Iterator begin() const { return ItemsContainerListRange::Iterator({ InventoryPlayerItemsRange(*player_).begin(), BeltPlayerItemsRange(*player_).begin(), }); } [[nodiscard]] ItemsContainerListRange::Iterator end() const { return ItemsContainerListRange::Iterator({ InventoryPlayerItemsRange(*player_).end(), BeltPlayerItemsRange(*player_).end(), }); } private: Player *player_; }; /** * @brief A range over non-empty player items in the following order: Equipped, Inventory, Belt. */ class PlayerItemsRange { public: explicit PlayerItemsRange(Player &player) : player_(&player) { } [[nodiscard]] ItemsContainerListRange::Iterator begin() const { return ItemsContainerListRange::Iterator({ EquippedPlayerItemsRange(*player_).begin(), InventoryPlayerItemsRange(*player_).begin(), BeltPlayerItemsRange(*player_).begin(), }); } [[nodiscard]] ItemsContainerListRange::Iterator end() const { return ItemsContainerListRange::Iterator({ EquippedPlayerItemsRange(*player_).end(), InventoryPlayerItemsRange(*player_).end(), BeltPlayerItemsRange(*player_).end(), }); } private: Player *player_; }; } // namespace devilution