@@ -6,6 +6,7 @@ add_library(libswan SHARED | |||
src/Game.cc | |||
src/gfxutil.cc | |||
src/Item.cc | |||
src/ItemStack.cc | |||
src/Mod.cc | |||
src/OS.cc | |||
src/Resource.cc |
@@ -12,15 +12,28 @@ public: | |||
struct Builder { | |||
std::string name; | |||
std::string image; | |||
int max_stack = 64; | |||
}; | |||
Item(const ResourceManager &resources, const Builder &builder): | |||
name_(builder.name), image_(resources.getImage(builder.image)) {} | |||
name_(builder.name), image_(resources.getImage(builder.image)), | |||
max_stack_(builder.max_stack) {} | |||
const std::string name_; | |||
const ImageResource &image_; | |||
const int max_stack_; | |||
static std::unique_ptr<Item> createInvalid(Context &ctx); | |||
// For testing, we want to be able to create Items without an actual ImageResource. | |||
// Tests can create a MockItem class which inherits from Item and uses this ctor, | |||
// as long as the test never does anything which tries to follow the image_ member. | |||
// Eventually, this should become unnecessary, because we don't need to require | |||
// a complete ImageResource for a headless server, but for now, this will suffice. | |||
protected: | |||
Item(const Builder &builder): | |||
name_(builder.name), image_(*(ImageResource *)this), | |||
max_stack_(builder.max_stack) {} | |||
}; | |||
} |
@@ -1,17 +1,11 @@ | |||
#pragma once | |||
#include <optional> | |||
#include "log.h" | |||
namespace Swan { | |||
class Item; | |||
class ItemStack { | |||
public: | |||
static constexpr int MAX_COUNT = 64; | |||
ItemStack(Item *item, int count = 0): item_(item), count_(count) { | |||
// We don't want a "partially empty" state. | |||
@@ -26,33 +20,7 @@ public: | |||
bool empty() { return item_ == nullptr; } | |||
// Insert as much of 'st' as possible, returning the leftovers | |||
ItemStack insert(ItemStack st) { | |||
// If this is an empty item stack, just copy over st | |||
if (empty()) { | |||
item_ = st.item_; | |||
count_ = st.count_; | |||
st.item_ = nullptr; | |||
st.count_ = 0; | |||
return st; | |||
} | |||
// If st is a stack of a different kind of item, we don't want it | |||
if (st.item_ != item_) | |||
return st; | |||
// Merge | |||
count_ += st.count_; | |||
if (count_ > MAX_COUNT) { | |||
st.count_ = count_ - MAX_COUNT; | |||
count_ = MAX_COUNT; | |||
} else { | |||
st.count_ = 0; | |||
st.item_ = nullptr; | |||
} | |||
return st; | |||
} | |||
ItemStack insert(ItemStack st); | |||
private: | |||
Item *item_; |
@@ -0,0 +1,35 @@ | |||
#include "ItemStack.h" | |||
#include "Item.h" | |||
namespace Swan { | |||
ItemStack ItemStack::insert(ItemStack st) { | |||
// If this is an empty item stack, just copy over st | |||
if (empty()) { | |||
item_ = st.item_; | |||
count_ = st.count_; | |||
st.item_ = nullptr; | |||
st.count_ = 0; | |||
return st; | |||
} | |||
// If st is a stack of a different kind of item, we don't want it | |||
if (st.item_ != item_) | |||
return st; | |||
// Merge | |||
count_ += st.count_; | |||
if (count_ > item_->max_stack_) { | |||
st.count_ = count_ - item_->max_stack_; | |||
count_ = item_->max_stack_; | |||
} else { | |||
st.count_ = 0; | |||
st.item_ = nullptr; | |||
} | |||
return st; | |||
} | |||
} |
@@ -1,17 +1,18 @@ | |||
#include "ItemStack.h" | |||
#include "Item.h" | |||
#include "lib/test.h" | |||
// Most of these tests need two ItemStacks. | |||
// ItemStack requires an Item pointer, and only ever looks at the pointer. | |||
// Therefore, we'll just give the ItemStacks pointers to ints. | |||
static int itint1, itint2; | |||
static Swan::Item *item1 = (Swan::Item *)&itint1; | |||
static Swan::Item *item2 = (Swan::Item *)&itint2; | |||
class MockItem: public Swan::Item { | |||
public: | |||
MockItem(const Builder &builder): Item(builder) {} | |||
}; | |||
test("Basic insert") { | |||
Swan::ItemStack s1(item1, 0); | |||
Swan::ItemStack s2(item1, 10); | |||
MockItem item1({ .name = "item1", .image = "no" }); | |||
Swan::ItemStack s1(&item1, 0); | |||
Swan::ItemStack s2(&item1, 10); | |||
s2 = s1.insert(s2); | |||
expecteq(s1.count(), 10); | |||
@@ -19,8 +20,11 @@ test("Basic insert") { | |||
} | |||
test("Insert rejects different items") { | |||
Swan::ItemStack s1(item1, 5); | |||
Swan::ItemStack s2(item2, 10); | |||
MockItem item1({ .name = "item1", .image = "no" }); | |||
MockItem item2({ .name = "itemm2", .image = "no" }); | |||
Swan::ItemStack s1(&item1, 5); | |||
Swan::ItemStack s2(&item2, 10); | |||
Swan::ItemStack ret = s1.insert(s2); | |||
expecteq(s1.count(), 5); | |||
@@ -30,18 +34,33 @@ test("Insert rejects different items") { | |||
} | |||
test("Insert never overflows") { | |||
Swan::ItemStack s1(item1, 40); | |||
Swan::ItemStack s2(item1, 40); | |||
MockItem item1({ .name = "item1", .image = "no" }); | |||
Swan::ItemStack s1(&item1, 40); | |||
Swan::ItemStack s2(&item1, 40); | |||
s2 = s1.insert(s2); | |||
expecteq(s1.count(), Swan::ItemStack::MAX_COUNT); | |||
expecteq(s2.count(), 80 - Swan::ItemStack::MAX_COUNT); | |||
expecteq(s1.count(), item1.max_stack_); | |||
expecteq(s2.count(), 80 - item1.max_stack_); | |||
} | |||
test("Insert respects max_stack_") { | |||
MockItem item1({ .name = "item1", .image = "no", .max_stack = 20 }); | |||
Swan::ItemStack s1(&item1, 15); | |||
Swan::ItemStack s2(&item1, 19); | |||
s2 = s1.insert(s2); | |||
expecteq(s1.count(), 20); | |||
expecteq(s2.count(), 14); | |||
} | |||
test("When insert empties an ItemStack, it should have its item nulled out") { | |||
Swan::ItemStack s1(item1, 10); | |||
Swan::ItemStack s2(item1, 10); | |||
MockItem item1({ .name = "item1", .image = "no" }); | |||
MockItem item2({ .name = "itemm2", .image = "no" }); | |||
Swan::ItemStack s1(&item1, 10); | |||
Swan::ItemStack s2(&item1, 10); | |||
s2 = s1.insert(s2); | |||
expecteq(s1.count(), 20); | |||
@@ -51,12 +70,14 @@ test("When insert empties an ItemStack, it should have its item nulled out") { | |||
} | |||
test("Insert on an empty item stack") { | |||
MockItem item1({ .name = "item1", .image = "no" }); | |||
Swan::ItemStack s1(nullptr, 0); | |||
Swan::ItemStack s2(item1, 10); | |||
Swan::ItemStack s2(&item1, 10); | |||
s2 = s1.insert(s2); | |||
expecteq(s1.count(), 10); | |||
expecteq(s1.item(), item1); | |||
expecteq(s1.item(), &item1); | |||
expecteq(s2.count(), 0); | |||
expect(s2.empty()); | |||
} |