osb/source/game/StarItemBag.cpp
Kai Blaschke 431a9c00a5
Fixed a huge amount of Clang warnings
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably.

99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified.

Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects.

Most remaining warnings are now unused parameters.
2024-02-19 16:55:19 +01:00

373 lines
8.7 KiB
C++

#include "StarItemBag.hpp"
#include "StarRoot.hpp"
#include "StarItemDatabase.hpp"
#include "StarJsonExtra.hpp"
namespace Star {
ItemBag::ItemBag() {}
ItemBag::ItemBag(size_t size) {
m_items.resize(size);
}
ItemBag ItemBag::fromJson(Json const& store) {
auto itemDatabase = Root::singleton().itemDatabase();
ItemBag res;
res.m_items = store.toArray().transformed([itemDatabase](Json const& v) { return itemDatabase->fromJson(v); });
return res;
}
ItemBag ItemBag::loadStore(Json const& store) {
auto itemDatabase = Root::singleton().itemDatabase();
ItemBag res;
res.m_items = store.toArray().transformed([itemDatabase](Json const& v) { return itemDatabase->diskLoad(v); });
return res;
}
Json ItemBag::toJson() const {
auto itemDatabase = Root::singleton().itemDatabase();
return m_items.transformed([itemDatabase](ItemConstPtr const& item) { return itemDatabase->toJson(item); });
}
Json ItemBag::diskStore() const {
auto itemDatabase = Root::singleton().itemDatabase();
return m_items.transformed([itemDatabase](ItemConstPtr const& item) { return itemDatabase->diskStore(item); });
}
size_t ItemBag::size() const {
return m_items.size();
}
List<ItemPtr> ItemBag::resize(size_t size) {
List<ItemPtr> lost;
while (m_items.size() > size) {
auto lastItem = m_items.takeLast();
lastItem = addItems(lastItem);
if (lastItem && !lastItem->empty())
lost.append(lastItem);
}
m_items.resize(size);
return lost;
}
void ItemBag::clearItems() {
size_t oldSize = m_items.size();
m_items.clear();
m_items.resize(oldSize);
}
bool ItemBag::cleanup() const {
bool cleanupDone = false;
for (auto& items : const_cast<ItemBag*>(this)->m_items) {
if (items && items->empty()) {
cleanupDone = true;
items = {};
}
}
return cleanupDone;
}
List<ItemPtr>& ItemBag::items() {
// When returning the entire item collection, need to make sure that there
// are no empty items before returning.
cleanup();
return m_items;
}
List<ItemPtr> const& ItemBag::items() const {
return const_cast<ItemBag*>(this)->items();
}
ItemPtr const& ItemBag::at(size_t i) const {
return const_cast<ItemBag*>(this)->at(i);
}
ItemPtr& ItemBag::at(size_t i) {
auto& item = m_items.at(i);
if (item && item->empty())
item = {};
return item;
}
List<ItemPtr> ItemBag::takeAll() {
List<ItemPtr> taken;
for (size_t i = 0; i < size(); ++i) {
if (auto& item = at(i))
taken.append(std::move(item));
}
return taken;
}
void ItemBag::setItem(size_t pos, ItemPtr item) {
auto& storedItem = at(pos);
storedItem = item;
}
ItemPtr ItemBag::putItems(size_t pos, ItemPtr items) {
if (!items || items->empty())
return {};
auto& storedItem = at(pos);
if (storedItem) {
// Try to stack with an item that is already there
storedItem->stackWith(items);
if (!items->empty())
return items;
else
return {};
} else {
// Otherwise just put the items there and return nothing.
storedItem = items;
return {};
}
}
ItemPtr ItemBag::takeItems(size_t pos, uint64_t count) {
if (auto& storedItem = at(pos)) {
auto taken = storedItem->take(count);
if (storedItem->empty())
storedItem = {};
return taken;
} else {
return {};
}
}
ItemPtr ItemBag::swapItems(size_t pos, ItemPtr items, bool tryCombine) {
auto& storedItem = at(pos);
auto swapItems = items;
if (!swapItems || swapItems->empty()) {
// If we are passed in nothing, simply return what's there, if anything.
swapItems = storedItem;
storedItem = {};
} else if (storedItem) {
// If something is there, try to stack with it first. If we can't stack,
// then swap.
if (!tryCombine || !storedItem->stackWith(swapItems))
std::swap(storedItem, swapItems);
} else {
// Otherwise just place the given items in the slot.
storedItem = swapItems;
swapItems = {};
}
return swapItems;
}
bool ItemBag::consumeItems(size_t pos, uint64_t count) {
bool consumed = false;
if (auto& storedItem = at(pos)) {
consumed = storedItem->consume(count);
if (storedItem->empty())
storedItem = {};
}
return consumed;
}
bool ItemBag::consumeItems(ItemDescriptor const& descriptor, bool exactMatch) {
uint64_t countLeft = descriptor.count();
List<std::pair<size_t, uint64_t>> consumeLocations;
for (size_t i = 0; i < m_items.size(); ++i) {
auto& storedItem = at(i);
if (storedItem && storedItem->matches(descriptor, exactMatch)) {
uint64_t count = storedItem->count();
uint64_t take = std::min(count, countLeft);
consumeLocations.append({i, take});
countLeft -= take;
if (countLeft == 0)
break;
}
}
// Only consume any if we can consume them all
if (countLeft > 0)
return false;
for (auto loc : consumeLocations) {
bool res = consumeItems(loc.first, loc.second);
_unused(res);
starAssert(res);
}
return true;
}
uint64_t ItemBag::available(ItemDescriptor const& descriptor, bool exactMatch) const {
uint64_t count = 0;
for (auto const& items : m_items) {
if (items && items->matches(descriptor, exactMatch))
count += items->count();
}
return count / descriptor.count();
}
uint64_t ItemBag::itemsCanFit(ItemConstPtr const& items) const {
auto itemsFit = itemsFitWhere(items);
return items->count() - itemsFit.leftover;
}
uint64_t ItemBag::itemsCanStack(ItemConstPtr const& items) const {
auto itemsFit = itemsFitWhere(items);
uint64_t stackable = 0;
for (auto slot : itemsFit.slots)
if (m_items[slot])
stackable += stackTransfer(at(slot), items);
return stackable;
}
auto ItemBag::itemsFitWhere(ItemConstPtr const& items, uint64_t max) const -> ItemsFitWhereResult {
if (!items || items->empty())
return ItemsFitWhereResult();
List<size_t> slots;
uint64_t count = std::min(items->count(), max);
while (true) {
if (count == 0)
break;
size_t slot = bestSlotAvailable(items, false);
if (slot == NPos)
break;
else
slots.append(slot);
uint64_t available = stackTransfer(at(slot), items);
if (available != 0)
count -= std::min(available, count);
else
break;
}
return ItemsFitWhereResult{count, slots};
}
ItemPtr ItemBag::addItems(ItemPtr items) {
if (!items || items->empty())
return {};
while (true) {
size_t slot = bestSlotAvailable(items, false);
if (slot == NPos)
return items;
auto& storedItem = at(slot);
if (storedItem) {
storedItem->stackWith(items);
if (items->empty())
return {};
} else {
storedItem = std::move(items);
return {};
}
}
}
ItemPtr ItemBag::stackItems(ItemPtr items) {
if (!items || items->empty())
return {};
while (true) {
size_t slot = bestSlotAvailable(items, true);
if (slot == NPos)
return items;
auto& storedItem = at(slot);
if (storedItem) {
storedItem->stackWith(items);
if (items->empty())
return {};
} else {
storedItem = std::move(items);
return {};
}
}
}
void ItemBag::condenseStacks() {
for (size_t i = size() - 1; i > 0; --i) {
if (auto& item = at(i)) {
for (size_t j = 0; j < i; j++) {
if (auto& stackWithItem = at(j))
item->stackWith(stackWithItem);
if (item->empty())
break;
}
}
}
}
void ItemBag::read(DataStream& ds) {
auto itemDatabase = Root::singleton().itemDatabase();
m_items.clear();
m_items.resize(ds.readVlqU());
size_t setItemsSize = ds.readVlqU();
for (size_t i = 0; i < setItemsSize; ++i)
itemDatabase->loadItem(ds.read<ItemDescriptor>(), at(i));
}
void ItemBag::write(DataStream& ds) const {
// Try not to write the whole bag if a large part of the end of the bag is
// empty.
ds.writeVlqU(m_items.size());
size_t setItemsSize = 0;
for (size_t i = 0; i < m_items.size(); ++i) {
if (at(i))
setItemsSize = i + 1;
}
ds.writeVlqU(setItemsSize);
for (size_t i = 0; i < setItemsSize; ++i)
ds.write(itemSafeDescriptor(at(i)));
}
uint64_t ItemBag::stackTransfer(ItemConstPtr const& to, ItemConstPtr const& from) {
if (!from)
return 0;
else if (!to)
return from->count();
else if (!to->stackableWith(from))
return 0;
else
return std::min(to->maxStack() - to->count(), from->count());
}
size_t ItemBag::bestSlotAvailable(ItemConstPtr const& item, bool stacksOnly) const {
// First look for any slots that can stack, before empty slots.
for (size_t i = 0; i < m_items.size(); ++i) {
auto const& storedItem = at(i);
if (storedItem && stackTransfer(storedItem, item) != 0)
return i;
}
if (!stacksOnly) {
// Then, look for any empty slots.
for (size_t i = 0; i < m_items.size(); ++i) {
if (!at(i))
return i;
}
}
return NPos;
}
}