Inventory (WIP part I)

This commit is contained in:
MihailRis 2024-01-21 15:23:46 +03:00
parent fe5cab0db0
commit 5142f3b4e7
13 changed files with 865 additions and 176 deletions

View File

@ -1,146 +1,334 @@
#include "InventoryView.h" #include "InventoryView.h"
#include <iostream>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "BlocksPreview.h" #include "BlocksPreview.h"
#include "LevelFrontend.h" #include "LevelFrontend.h"
#include "../window/Events.h" #include "../window/Events.h"
#include "../window/input.h"
#include "../assets/Assets.h" #include "../assets/Assets.h"
#include "../graphics/Atlas.h" #include "../graphics/Atlas.h"
#include "../graphics/Shader.h" #include "../graphics/Shader.h"
#include "../graphics/Batch2D.h" #include "../graphics/Batch2D.h"
#include "../graphics/GfxContext.h" #include "../graphics/GfxContext.h"
#include "../graphics/Font.h"
#include "../content/Content.h" #include "../content/Content.h"
#include "../items/ItemDef.h" #include "../items/ItemDef.h"
#include "../items/Inventory.h"
#include "../maths/voxmaths.h" #include "../maths/voxmaths.h"
#include "../objects/Player.h" #include "../objects/Player.h"
#include "../voxels/Block.h" #include "../voxels/Block.h"
#include "../frontend/gui/controls.h"
#include "../util/stringutil.h"
InventoryLayout::InventoryLayout(glm::vec2 size) : size(size) {}
void InventoryLayout::add(SlotLayout slot) {
slots.push_back(slot);
}
void InventoryLayout::setSize(glm::vec2 size) {
this->size = size;
}
void InventoryLayout::setOrigin(glm::vec2 origin) {
this->origin = origin;
}
glm::vec2 InventoryLayout::getSize() const {
return size;
}
glm::vec2 InventoryLayout::getOrigin() const {
return origin;
}
std::vector<SlotLayout>& InventoryLayout::getSlots() {
return slots;
}
SlotLayout::SlotLayout(
glm::vec2 position,
bool background,
bool itemSource,
itemsharefunc shareFunc,
slotcallback rightClick
)
: position(position),
background(background),
itemSource(itemSource),
shareFunc(shareFunc),
rightClick(rightClick) {}
InventoryPanel::InventoryPanel(
glm::vec2 position,
glm::vec2 size,
glm::vec4 color)
: position(position), size(size), color(color) {}
InventoryBuilder::InventoryBuilder()
: layout(std::make_unique<InventoryLayout>(glm::vec2()))
{}
void InventoryBuilder::addGrid(
int cols, int rows,
glm::vec2 coord,
int padding,
SlotLayout slotLayout)
{
const int slotSize = InventoryView::SLOT_SIZE;
const int interval = InventoryView::SLOT_INTERVAL;
uint width = cols * (slotSize + interval) - interval + padding*2;
uint height = rows * (slotSize + interval) - interval + padding*2;
auto lsize = layout->getSize();
if (coord.x + width > lsize.x) {
lsize.x = coord.x + width;
}
if (coord.y + height > lsize.y) {
lsize.y = coord.y + height;
}
layout->setSize(lsize);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
glm::vec2 position (
col * (slotSize + interval) + padding,
row * (slotSize + interval) + padding
);
auto builtSlot = slotLayout;
builtSlot.position = position;
layout->add(builtSlot);
}
}
}
std::unique_ptr<InventoryLayout> InventoryBuilder::build() {
return std::unique_ptr<InventoryLayout>(layout.release());
}
SlotView::SlotView(
ItemStack& stack,
LevelFrontend* frontend,
InventoryInteraction* interaction,
const Content* content,
SlotLayout layout)
: UINode(glm::vec2(), glm::vec2(InventoryView::SLOT_SIZE)),
frontend(frontend),
interaction(interaction),
content(content),
stack(stack),
layout(layout) {
color(glm::vec4(0, 0, 0, 0.2f));
}
// performance disaster
void SlotView::draw(Batch2D* batch, Assets* assets) {
glm::vec2 coord = calcCoord();
int slotSize = InventoryView::SLOT_SIZE;
glm::vec4 tint(1.0f);
glm::vec4 color = color_;
if (hover_ || highlighted) {
tint *= 1.333f;
color = glm::vec4(1, 1, 1, 0.2f);
}
batch->color = color;
if (color.a > 0.0) {
batch->texture(nullptr);
if (highlighted) {
batch->rect(coord.x-4, coord.y-4, slotSize+8, slotSize+8);
} else {
batch->rect(coord.x, coord.y, slotSize, slotSize);
}
}
batch->color = glm::vec4(1.0f);
Shader* uiShader = assets->getShader("ui");
Viewport viewport(Window::width, Window::height);
GfxContext ctx(nullptr, viewport, batch);
auto preview = frontend->getBlocksPreview();
auto indices = content->getIndices();
ItemDef* item = indices->getItemDef(stack.getItemId());
switch (item->iconType) {
case item_icon_type::none:
break;
case item_icon_type::block:
batch->render();
{
GfxContext subctx = ctx.sub();
subctx.depthTest(true);
subctx.cullFace(true);
Block* cblock = content->requireBlock(item->icon);
preview->begin(&subctx.getViewport());
preview->draw(cblock, coord.x, coord.y, slotSize, tint);
}
uiShader->use();
batch->begin();
break;
case item_icon_type::sprite: {
size_t index = item->icon.find(':');
std::string name = item->icon.substr(index+1);
UVRegion region(0.0f, 0.0, 1.0f, 1.0f);
if (index == std::string::npos) {
batch->texture(assets->getTexture(name));
} else {
std::string atlasname = item->icon.substr(0, index);
Atlas* atlas = assets->getAtlas(atlasname);
if (atlas && atlas->has(name)) {
region = atlas->get(name);
batch->texture(atlas->getTexture());
}
}
batch->rect(
coord.x, coord.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
break;
}
}
if (stack.getCount() > 1) {
auto font = assets->getFont("normal");
std::wstring text = std::to_wstring(stack.getCount());
int x = coord.x+slotSize-text.length()*8;
int y = coord.y+slotSize-16;
batch->color = glm::vec4(0, 0, 0, 1.0f);
font->draw(batch, text, x+1, y+1);
batch->color = glm::vec4(1.0f);
font->draw(batch, text, x, y);
}
}
void SlotView::setHighlighted(bool flag) {
highlighted = flag;
}
bool SlotView::isHighlighted() const {
return highlighted;
}
void SlotView::clicked(gui::GUI* gui, int button) {
ItemStack& grabbed = interaction->getGrabbedItem();
if (button == mousecode::BUTTON_1) {
if (Events::pressed(keycode::LEFT_SHIFT)) {
if (layout.shareFunc) {
layout.shareFunc(stack);
}
return;
}
if (!layout.itemSource && stack.accepts(grabbed)) {
stack.move(grabbed, content->getIndices());
} else {
if (layout.itemSource) {
if (grabbed.isEmpty()) {
grabbed.set(stack);
} else {
grabbed.clear();
}
} else {
std::swap(grabbed, stack);
}
}
} else if (button == mousecode::BUTTON_2) {
if (layout.rightClick) {
layout.rightClick(stack, grabbed);
return;
}
if (layout.itemSource)
return;
if (grabbed.isEmpty()) {
if (!stack.isEmpty()) {
grabbed.set(stack);
int halfremain = stack.getCount() / 2;
grabbed.setCount(stack.getCount() - halfremain);
stack.setCount(halfremain);
}
} else {
if (stack.isEmpty()) {
stack.set(grabbed);
stack.setCount(1);
} else {
stack.setCount(stack.getCount()+1);
}
grabbed.setCount(grabbed.getCount()-1);
}
}
}
InventoryView::InventoryView( InventoryView::InventoryView(
int columns,
const Content* content, const Content* content,
LevelFrontend* frontend, LevelFrontend* frontend,
std::vector<itemid_t> items) InventoryInteraction* interaction,
: content(content), std::shared_ptr<Inventory> inventory,
std::unique_ptr<InventoryLayout> layout)
: Container(glm::vec2(), glm::vec2()),
content(content),
indices(content->getIndices()), indices(content->getIndices()),
items(items), inventory(inventory),
layout(std::move(layout)),
frontend(frontend), frontend(frontend),
columns(columns) { interaction(interaction) {
size(this->layout->getSize());
color(glm::vec4(0, 0, 0, 0.5f));
} }
InventoryView::~InventoryView() { InventoryView::~InventoryView() {}
}
void InventoryView::setPosition(int x, int y) { void InventoryView::build() {
position.x = x; int index = 0;
position.y = y; for (auto& slot : layout->getSlots()) {
} ItemStack& item = inventory->getSlot(index);
int InventoryView::getWidth() const { auto view = std::make_shared<SlotView>(
return columns * iconSize + (columns-1) * interval + padding.x * 2; item, frontend, interaction, content, slot
} );
if (!slot.background) {
int InventoryView::getHeight() const { view->color(glm::vec4());
uint inv_rows = ceildiv(items.size(), columns); }
return inv_rows * iconSize + (inv_rows-1) * interval + padding.y * 2; slots.push_back(view.get());
} add(view, slot.position);
index++;
void InventoryView::setSlotConsumer(slotconsumer consumer) {
this->consumer = consumer;
}
void InventoryView::setItems(std::vector<itemid_t> items) {
this->items = items;
}
void InventoryView::actAndDraw(const GfxContext* ctx) {
Assets* assets = frontend->getAssets();
Shader* uiShader = assets->getShader("ui");
auto viewport = ctx->getViewport();
uint inv_w = getWidth();
uint inv_h = getHeight();
int xs = position.x + padding.x;
int ys = position.y + padding.y;
glm::vec4 tint (1.0f);
int mx = Events::cursor.x;
int my = Events::cursor.y;
// background
auto batch = ctx->getBatch2D();
batch->texture(nullptr);
batch->color = glm::vec4(0.0f, 0.0f, 0.0f, 0.5f);
batch->rect(position.x, position.y, inv_w, inv_h);
batch->render();
// blocks & items
if (Events::scroll) {
scroll -= Events::scroll * (iconSize+interval);
} }
scroll = std::min(scroll, int(inv_h-viewport.getHeight())); }
scroll = std::max(scroll, 0);
void InventoryView::setSelected(int index) {
auto blocksPreview = frontend->getBlocksPreview(); for (int i = 0; i < int(slots.size()); i++) {
// todo: optimize auto slot = slots[i];
{ slot->setHighlighted(i == index);
Window::clearDepth(); }
GfxContext subctx = ctx->sub(); }
subctx.depthTest(true);
subctx.cullFace(true); void InventoryView::setCoord(glm::vec2 coord) {
uint index = 0; Container::setCoord(coord - layout->getOrigin());
for (uint i = 0; i < items.size(); i++) { }
ItemDef* item = indices->getItemDef(items[i]);
int x = xs + (iconSize+interval) * (index % columns); void InventoryView::setInventory(std::shared_ptr<Inventory> inventory) {
int y = ys + (iconSize+interval) * (index / columns) - scroll; this->inventory = inventory;
if (y < -int(iconSize+interval) || y >= int(viewport.getHeight())) { }
index++;
continue; InventoryLayout* InventoryView::getLayout() const {
} return layout.get();
if (mx > x && mx < x + (int)iconSize && my > y && my < y + (int)iconSize) { }
tint.r *= 1.2f;
tint.g *= 1.2f; // performance disaster x2
tint.b *= 1.2f; void InventoryView::draw(Batch2D* batch, Assets* assets) {
if (Events::jclicked(mousecode::BUTTON_1)) { Container::draw(batch, assets);
if (consumer) { Window::clearDepth();
consumer(items[i]); }
}
} void InventoryView::drawBackground(Batch2D* batch, Assets* assets) {
} else { glm::vec2 coord = calcCoord();
tint = glm::vec4(1.0f); batch->texture(nullptr);
} batch->color = color_;
switch (item->iconType) { batch->rect(coord.x-1, coord.y-1, size_.x+2, size_.y+2);
case item_icon_type::none:
break;
case item_icon_type::block: {
Block* cblock = content->requireBlock(item->icon);
blocksPreview->begin(&ctx->getViewport());
blocksPreview->draw(cblock, x, y, iconSize, tint);
break;
}
case item_icon_type::sprite: {
batch->begin();
uiShader->use();
size_t index = item->icon.find(':');
std::string name = item->icon.substr(index+1);
UVRegion region(0.0f, 0.0, 1.0f, 1.0f);
if (index == std::string::npos) {
batch->texture(assets->getTexture(name));
} else {
std::string atlasname = item->icon.substr(0, index);
Atlas* atlas = assets->getAtlas(atlasname);
if (atlas && atlas->has(name)) {
region = atlas->get(name);
batch->texture(atlas->getTexture());
}
}
batch->rect(x, y, 48, 48, 0, 0, 0, region, false, true, glm::vec4(1.0f));
batch->render();
break;
}
}
index++;
}
}
uiShader->use();
} }

View File

@ -5,46 +5,150 @@
#include <functional> #include <functional>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "../frontend/gui/UINode.h"
#include "../frontend/gui/panels.h"
#include "../frontend/gui/controls.h"
#include "../items/ItemStack.h"
#include "../typedefs.h" #include "../typedefs.h"
class Batch2D;
class Assets; class Assets;
class GfxContext; class GfxContext;
class Content; class Content;
class ContentIndices; class ContentIndices;
class LevelFrontend; class LevelFrontend;
class Inventory;
typedef std::function<void(itemid_t)> slotconsumer; typedef std::function<void(ItemStack&)> itemsharefunc;
typedef std::function<void(ItemStack&, ItemStack&)> slotcallback;
class InventoryView { class InventoryInteraction;
struct SlotLayout {
glm::vec2 position;
bool background;
bool itemSource;
itemsharefunc shareFunc;
slotcallback rightClick;
SlotLayout(glm::vec2 position,
bool background,
bool itemSource,
itemsharefunc shareFunc,
slotcallback rightClick);
};
// temporary unused
struct InventoryPanel {
glm::vec2 position;
glm::vec2 size;
glm::vec4 color;
InventoryPanel(glm::vec2 position,
glm::vec2 size,
glm::vec4 color);
};
class InventoryLayout {
glm::vec2 size;
glm::vec2 origin;
std::vector<SlotLayout> slots;
public:
InventoryLayout(glm::vec2 size);
void add(SlotLayout slot);
void setSize(glm::vec2 size);
void setOrigin(glm::vec2 origin);
glm::vec2 getSize() const;
glm::vec2 getOrigin() const;
std::vector<SlotLayout>& getSlots();
};
class InventoryBuilder {
std::unique_ptr<InventoryLayout> layout;
public:
InventoryBuilder();
void addGrid(
int cols, int rows,
glm::vec2 coord,
int padding,
SlotLayout slotLayout);
std::unique_ptr<InventoryLayout> build();
};
class SlotView : public gui::UINode {
LevelFrontend* frontend;
InventoryInteraction* interaction;
const Content* const content;
ItemStack& stack;
bool highlighted = false;
SlotLayout layout;
public:
SlotView(ItemStack& stack,
LevelFrontend* frontend,
InventoryInteraction* interaction,
const Content* content,
SlotLayout layout);
virtual void draw(Batch2D* batch, Assets* assets) override;
void setHighlighted(bool flag);
bool isHighlighted() const;
virtual void clicked(gui::GUI*, int) override;
};
class InventoryView : public gui::Container {
const Content* content; const Content* content;
const ContentIndices* indices; const ContentIndices* indices;
std::vector<itemid_t> items;
slotconsumer consumer = nullptr; std::shared_ptr<Inventory> inventory;
std::unique_ptr<InventoryLayout> layout;
LevelFrontend* frontend; LevelFrontend* frontend;
InventoryInteraction* interaction;
std::vector<SlotView*> slots;
int scroll = 0; int scroll = 0;
int columns;
uint iconSize = 48;
uint interval = 4;
glm::ivec2 padding {interval, interval};
glm::ivec2 position {0, 0};
public: public:
InventoryView( InventoryView(
int columns,
const Content* content, const Content* content,
LevelFrontend* frontend, LevelFrontend* frontend,
std::vector<itemid_t> items); InventoryInteraction* interaction,
std::shared_ptr<Inventory> inventory,
std::unique_ptr<InventoryLayout> layout);
virtual ~InventoryView(); virtual ~InventoryView();
virtual void actAndDraw(const GfxContext* ctx); void build();
void setItems(std::vector<itemid_t> items); virtual void draw(Batch2D* batch, Assets* assets) override;
virtual void drawBackground(Batch2D* batch, Assets* assets) override;
void setPosition(int x, int y); void setInventory(std::shared_ptr<Inventory> inventory);
int getWidth() const;
int getHeight() const; virtual void setCoord(glm::vec2 coord) override;
void setSlotConsumer(slotconsumer consumer);
InventoryLayout* getLayout() const;
void setSelected(int index);
static const int SLOT_INTERVAL = 4;
static const int SLOT_SIZE = 48;
};
class InventoryInteraction {
ItemStack grabbedItem;
public:
InventoryInteraction() = default;
ItemStack& getGrabbedItem() {
return grabbedItem;
}
}; };
#endif // FRONTEND_INVENTORY_VIEW_H_ #endif // FRONTEND_INVENTORY_VIEW_H_

View File

@ -28,6 +28,8 @@
#include "../settings.h" #include "../settings.h"
#include "../engine.h" #include "../engine.h"
#include "../items/ItemDef.h" #include "../items/ItemDef.h"
#include "../items/ItemStack.h"
#include "../items/Inventory.h"
#include "LevelFrontend.h" #include "LevelFrontend.h"
#include "graphics/Skybox.h" #include "graphics/Skybox.h"
@ -169,8 +171,10 @@ void WorldRenderer::draw(const GfxContext& pctx, Camera* camera, bool hudVisible
shader->uniform3f("u_cameraPos", camera->position); shader->uniform3f("u_cameraPos", camera->position);
shader->uniform1i("u_cubemap", 1); shader->uniform1i("u_cubemap", 1);
{ {
itemid_t id = level->player->getChosenItem(); auto player = level->player;
ItemDef* item = indices->getItemDef(id); auto inventory = player->getInventory();
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
ItemDef* item = indices->getItemDef(stack.getItemId());
assert(item != nullptr); assert(item != nullptr);
float multiplier = 0.5f; float multiplier = 0.5f;
shader->uniform3f("u_torchlightColor", shader->uniform3f("u_torchlightColor",

View File

@ -10,8 +10,6 @@ using gui::Align;
using glm::vec2; using glm::vec2;
using glm::vec4; using glm::vec4;
#include <iostream>
UINode::UINode(vec2 coord, vec2 size) : coord(coord), size_(size) { UINode::UINode(vec2 coord, vec2 size) : coord(coord), size_(size) {
} }
@ -85,7 +83,7 @@ shared_ptr<UINode> UINode::getAt(vec2 pos, shared_ptr<UINode> self) {
} }
bool UINode::isInteractive() const { bool UINode::isInteractive() const {
return interactive; return interactive && visible();
} }
void UINode::setInteractive(bool flag) { void UINode::setInteractive(bool flag) {

View File

@ -44,6 +44,7 @@
#include "../engine.h" #include "../engine.h"
#include "../core_defs.h" #include "../core_defs.h"
#include "../items/ItemDef.h" #include "../items/ItemDef.h"
#include "../items/Inventory.h"
using glm::vec2; using glm::vec2;
using glm::vec3; using glm::vec3;
@ -163,26 +164,142 @@ void HudRenderer::createDebugPanel(Engine* engine) {
panel->refresh(); panel->refresh();
} }
std::shared_ptr<InventoryView> HudRenderer::createContentAccess() {
auto level = frontend->getLevel();
auto content = level->content;
auto indices = content->getIndices();
auto player = level->player;
auto inventory = player->getInventory();
int itemsCount = indices->countItemDefs();
auto accessInventory = std::make_shared<Inventory>(itemsCount);
for (int id = 1; id < itemsCount; id++) {
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
}
const int slotSize = InventoryView::SLOT_SIZE;
const int interval = InventoryView::SLOT_INTERVAL;
int padding = 8;
int columns = 8;
int rows = ceildiv(itemsCount-1, columns);
uint cawidth = columns * (slotSize + interval) - interval + padding;
uint caheight = rows * (slotSize + interval) - interval + padding*2;
auto layout = std::make_unique<InventoryLayout>(glm::vec2(cawidth, caheight));
for (int i = 0; i < itemsCount-1; i++) {
int row = i / columns;
int col = i % columns;
glm::vec2 position (
col * slotSize + (col-1) * interval + padding,
row * slotSize + (row-1) * interval + padding
);
layout->add(SlotLayout(position, false, true,
[=](ItemStack& item) {
auto copy = ItemStack(item);
inventory->move(copy, indices);
},
[=](ItemStack& item, ItemStack& grabbed) {
inventory->getSlot(player->getChosenSlot()).set(item);
}));
}
auto contentAccess = std::make_shared<InventoryView>(
content,
frontend,
interaction.get(),
accessInventory,
std::move(layout)
);
contentAccess->build();
return contentAccess;
}
std::shared_ptr<InventoryView> HudRenderer::createHotbar() {
auto level = frontend->getLevel();
auto player = level->player;
auto inventory = player->getInventory();
auto content = level->content;
const int slotSize = InventoryView::SLOT_SIZE;
const int interval = InventoryView::SLOT_INTERVAL;
int padding = 4;
uint width = 10 * (slotSize + interval) - interval + padding*2;
uint height = slotSize + padding * 2;
auto layout = std::make_unique<InventoryLayout>(glm::vec2(width, height));
for (int i = 0; i < 10; i++) {
glm::vec2 position (i * (slotSize + interval) + padding, padding);
layout->add(SlotLayout(position, false, false, nullptr, nullptr));
}
layout->setOrigin(glm::vec2(width / 2, 0));
auto view = std::make_shared<InventoryView>(
content,
frontend,
interaction.get(),
inventory,
std::move(layout)
);
view->build();
view->setInteractive(false);
return view;
}
std::shared_ptr<InventoryView> HudRenderer::createInventory() {
auto level = frontend->getLevel();
auto player = level->player;
auto inventory = player->getInventory();
auto content = level->content;
SlotLayout slotLayout(glm::vec2(), true, false, [=](ItemStack& stack) {
stack.clear();
}, nullptr);
int columns = 10;
int rows = ceildiv(inventory->size(), columns);
int padding = 4;
InventoryBuilder builder;
builder.addGrid(columns, rows, glm::vec2(), padding, slotLayout);
auto layout = builder.build();
auto view = std::make_shared<InventoryView>(
content,
frontend,
interaction.get(),
inventory,
std::move(layout)
);
view->build();
return view;
}
HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend) HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
: assets(engine->getAssets()), : assets(engine->getAssets()),
gui(engine->getGUI()), gui(engine->getGUI()),
frontend(frontend) { frontend(frontend)
{
auto level = frontend->getLevel();
auto menu = gui->getMenu(); auto menu = gui->getMenu();
auto content = level->content;
auto indices = content->getIndices();
std::vector<itemid_t> items; interaction = std::make_unique<InventoryInteraction>();
for (itemid_t id = 1; id < indices->countItemDefs(); id++) { grabbedItemView = std::make_shared<SlotView>(
items.push_back(id); interaction->getGrabbedItem(),
} frontend,
contentAccess.reset(new InventoryView(8, content, frontend, items)); interaction.get(),
contentAccess->setSlotConsumer([=](blockid_t id) { frontend->getLevel()->content,
level->player->setChosenItem(id); SlotLayout(glm::vec2(), false, false, nullptr, nullptr)
}); );
grabbedItemView->color(glm::vec4());
grabbedItemView->setInteractive(false);
hotbarView.reset(new InventoryView(1, content, frontend, std::vector<itemid_t> {0})); contentAccess = createContentAccess();
contentAccessPanel = std::make_shared<Panel>(
contentAccess->size(), vec4(0.0f), 0.0f
);
contentAccessPanel->color(glm::vec4());
contentAccessPanel->add(contentAccess);
contentAccessPanel->scrollable(true);
hotbarView = createHotbar();
inventoryView = createInventory();
uicamera = new Camera(vec3(), 1); uicamera = new Camera(vec3(), 1);
uicamera->perspective = false; uicamera->perspective = false;
@ -191,10 +308,18 @@ HudRenderer::HudRenderer(Engine* engine, LevelFrontend* frontend)
createDebugPanel(engine); createDebugPanel(engine);
menu->reset(); menu->reset();
gui->add(this->debugPanel); gui->add(debugPanel);
gui->add(contentAccessPanel);
gui->add(hotbarView);
gui->add(inventoryView);
gui->add(grabbedItemView);
} }
HudRenderer::~HudRenderer() { HudRenderer::~HudRenderer() {
gui->remove(grabbedItemView);
gui->remove(inventoryView);
gui->remove(hotbarView);
gui->remove(contentAccessPanel);
gui->remove(debugPanel); gui->remove(debugPanel);
delete uicamera; delete uicamera;
} }
@ -206,7 +331,12 @@ void HudRenderer::drawDebug(int fps){
} }
void HudRenderer::update(bool visible) { void HudRenderer::update(bool visible) {
auto level = frontend->getLevel();
auto player = level->player;
auto menu = gui->getMenu(); auto menu = gui->getMenu();
menu->visible(pause);
if (!visible && inventoryOpen) { if (!visible && inventoryOpen) {
inventoryOpen = false; inventoryOpen = false;
} }
@ -232,13 +362,35 @@ void HudRenderer::update(bool visible) {
if ((pause || inventoryOpen) == Events::_cursor_locked) { if ((pause || inventoryOpen) == Events::_cursor_locked) {
Events::toggleCursor(); Events::toggleCursor();
} }
glm::vec2 invSize = contentAccessPanel->size();
inventoryView->visible(inventoryOpen);
contentAccessPanel->visible(inventoryOpen);
contentAccessPanel->size(glm::vec2(invSize.x, Window::height));
for (int i = keycode::NUM_1; i <= keycode::NUM_9; i++) {
if (Events::jpressed(i)) {
player->setChosenSlot(i - keycode::NUM_1);
}
}
if (Events::jpressed(keycode::NUM_0)) {
player->setChosenSlot(9);
}
if (!inventoryOpen && Events::scroll) {
int slot = player->getChosenSlot();
slot = (slot + Events::scroll) % 10;
if (slot < 0) {
slot += 10;
}
player->setChosenSlot(slot);
}
} }
void HudRenderer::drawOverlay(const GfxContext& ctx) { void HudRenderer::drawOverlay(const GfxContext& ctx) {
if (pause) { if (pause) {
Shader* uishader = assets->getShader("ui"); Shader* uishader = assets->getShader("ui");
uishader->use(); uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjection()*uicamera->getView()); uishader->uniformMatrix("u_projview", uicamera->getProjView());
const Viewport& viewport = ctx.getViewport(); const Viewport& viewport = ctx.getViewport();
const uint width = viewport.getWidth(); const uint width = viewport.getWidth();
@ -271,12 +423,11 @@ void HudRenderer::draw(const GfxContext& ctx){
Shader* uishader = assets->getShader("ui"); Shader* uishader = assets->getShader("ui");
uishader->use(); uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjection()*uicamera->getView()); uishader->uniformMatrix("u_projview", uicamera->getProjView());
// Draw selected item preview // Draw selected item preview
hotbarView->setPosition(width-60, height-60); hotbarView->setCoord(glm::vec2(width/2, height-65));
hotbarView->setItems({player->getChosenItem()}); hotbarView->setSelected(player->getChosenSlot());
hotbarView->actAndDraw(&ctx);
// Crosshair // Crosshair
batch->begin(); batch->begin();
@ -289,10 +440,20 @@ void HudRenderer::draw(const GfxContext& ctx){
} }
if (inventoryOpen) { if (inventoryOpen) {
// draw content access panel (all available items) auto caLayout = contentAccess->getLayout();
contentAccess->setPosition(viewport.getWidth()-contentAccess->getWidth(), 0); auto invLayout = inventoryView->getLayout();
contentAccess->actAndDraw(&ctx); float caWidth = caLayout->getSize().x;
glm::vec2 invSize = invLayout->getSize();
float width = viewport.getWidth();
inventoryView->setCoord(glm::vec2(
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
height/2-invSize.y/2
));
contentAccessPanel->setCoord(glm::vec2(width-caWidth, 0));
} }
grabbedItemView->setCoord(glm::vec2(Events::cursor));
batch->render(); batch->render();
} }

View File

@ -15,12 +15,15 @@ class Assets;
class Player; class Player;
class Level; class Level;
class Engine; class Engine;
class SlotView;
class InventoryView; class InventoryView;
class LevelFrontend; class LevelFrontend;
class InventoryInteraction;
namespace gui { namespace gui {
class GUI; class GUI;
class UINode; class UINode;
class Panel;
} }
class HudRenderer { class HudRenderer {
@ -34,13 +37,21 @@ class HudRenderer {
bool inventoryOpen = false; bool inventoryOpen = false;
bool pause = false; bool pause = false;
std::unique_ptr<InventoryView> contentAccess; std::shared_ptr<gui::Panel> contentAccessPanel;
std::unique_ptr<InventoryView> hotbarView; std::shared_ptr<InventoryView> contentAccess;
std::shared_ptr<InventoryView> hotbarView;
std::shared_ptr<InventoryView> inventoryView;
std::shared_ptr<gui::UINode> debugPanel; std::shared_ptr<gui::UINode> debugPanel;
std::unique_ptr<InventoryInteraction> interaction;
std::shared_ptr<SlotView> grabbedItemView;
gui::GUI* gui; gui::GUI* gui;
LevelFrontend* frontend; LevelFrontend* frontend;
void createDebugPanel(Engine* engine); void createDebugPanel(Engine* engine);
std::shared_ptr<InventoryView> createContentAccess();
std::shared_ptr<InventoryView> createHotbar();
std::shared_ptr<InventoryView> createInventory();
public: public:
HudRenderer(Engine* engine, LevelFrontend* frontend); HudRenderer(Engine* engine, LevelFrontend* frontend);
~HudRenderer(); ~HudRenderer();

74
src/items/Inventory.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "Inventory.h"
Inventory::Inventory(size_t size) : slots(size) {
}
ItemStack& Inventory::getSlot(size_t index) {
return slots[index];
}
size_t Inventory::findEmptySlot(size_t begin, size_t end) const {
end = std::min(slots.size(), end);
for (size_t i = begin; i < end; i++) {
if (slots[i].isEmpty()) {
return i;
}
}
return npos;
}
size_t Inventory::findSlotByItem(itemid_t id, size_t begin, size_t end) {
end = std::min(slots.size(), end);
for (size_t i = begin; i < end; i++) {
if (slots[i].getItemId() == id) {
return i;
}
}
return npos;
}
void Inventory::move(
ItemStack& item,
const ContentIndices* indices,
size_t begin,
size_t end)
{
end = std::min(slots.size(), end);
for (size_t i = begin; i < end && !item.isEmpty(); i++) {
ItemStack& slot = slots[i];
if (slot.accepts(item)) {
slot.move(item, indices);
}
}
}
void Inventory::read(const dynamic::Map* src) {
auto slotsarr = src->list("slots");
size_t slotscount = std::min(slotsarr->size(), slots.size());
for (size_t i = 0; i < slotscount; i++) {
auto item = slotsarr->map(i);
itemid_t id = item->getInt("id", ITEM_EMPTY);
itemcount_t count = item->getInt("count", 0);
auto& slot = slots[i];
slot.set(ItemStack(id, count));
}
}
std::unique_ptr<dynamic::Map> Inventory::write() const {
auto map = std::make_unique<dynamic::Map>();
auto& slotsarr = map->putList("slots");
for (size_t i = 0; i < slots.size(); i++) {
auto& item = slots[i];
itemid_t id = item.getItemId();
itemcount_t count = item.getCount();
auto& slotmap = slotsarr.putMap();
slotmap.put("id", id);
if (count) {
slotmap.put("count", count);
}
}
return map;
}
const size_t Inventory::npos = -1;

41
src/items/Inventory.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef ITEMS_INVENTORY_H_
#define ITEMS_INVENTORY_H_
#include <vector>
#include <memory>
#include "ItemStack.h"
#include "../typedefs.h"
#include "../data/dynamic.h"
class ContentIndices;
class Inventory {
std::vector<ItemStack> slots;
public:
Inventory(size_t size);
ItemStack& getSlot(size_t index);
size_t findEmptySlot(size_t begin=0, size_t end=-1) const;
size_t findSlotByItem(itemid_t id, size_t begin=0, size_t end=-1);
inline size_t size() const {
return slots.size();
}
void move(
ItemStack& item,
const ContentIndices* indices,
size_t begin=0,
size_t end=-1);
/* deserializing inventory */
void read(const dynamic::Map* src);
/* serializing inventory */
std::unique_ptr<dynamic::Map> write() const;
static const size_t npos;
};
#endif // ITEMS_INVENTORY_H_

40
src/items/ItemStack.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "ItemStack.h"
#include "ItemDef.h"
#include "../content/Content.h"
ItemStack::ItemStack() : item(ITEM_EMPTY), count(0) {
}
ItemStack::ItemStack(itemid_t item, itemcount_t count) : item(item), count(count) {
}
void ItemStack::set(const ItemStack& item) {
this->item = item.item;
this->count = item.count;
}
bool ItemStack::accepts(const ItemStack& other) const {
if (isEmpty()) {
return true;
}
return item == other.getItemId();
}
void ItemStack::move(ItemStack& item, const ContentIndices* indices) {
auto def = indices->getItemDef(item.getItemId());
int count = std::min(item.count, def->stackSize-this->count);
if (isEmpty()) {
set(ItemStack(item.getItemId(), count));
} else {
setCount(this->count + count);
}
item.setCount(item.count-count);
}
void ItemStack::setCount(itemcount_t count) {
this->count = count;
if (count == 0) {
item = 0;
}
}

40
src/items/ItemStack.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef ITEMS_ITEM_STACK_H_
#define ITEMS_ITEM_STACK_H_
#include "../typedefs.h"
#include "../constants.h"
class ContentIndices;
class ItemStack {
itemid_t item;
itemcount_t count;
public:
ItemStack();
ItemStack(itemid_t item, itemcount_t count);
void set(const ItemStack& item);
void setCount(itemcount_t count);
bool accepts(const ItemStack& item) const;
void move(ItemStack& item, const ContentIndices* indices);
inline void clear() {
set(ItemStack(0, 0));
}
inline bool isEmpty() const {
return item == ITEM_EMPTY;
}
inline itemid_t getItemId() const {
return item;
}
inline itemcount_t getCount() const {
return count;
}
};
#endif // ITEMS_ITEM_STACK_H_

View File

@ -13,6 +13,8 @@
#include "../window/Events.h" #include "../window/Events.h"
#include "../window/input.h" #include "../window/input.h"
#include "../items/ItemDef.h" #include "../items/ItemDef.h"
#include "../items/ItemStack.h"
#include "../items/Inventory.h"
#include "scripting/scripting.h" #include "scripting/scripting.h"
#include "BlocksController.h" #include "BlocksController.h"
@ -239,7 +241,9 @@ void PlayerController::updateInteraction(){
int z = iend.z; int z = iend.z;
uint8_t states = 0; uint8_t states = 0;
ItemDef* item = indices->getItemDef(player->getChosenItem()); auto inventory = player->getInventory();
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
ItemDef* item = indices->getItemDef(stack.getItemId());
Block* def = indices->getBlockDef(item->rt.placingBlock); Block* def = indices->getBlockDef(item->rt.placingBlock);
if (def && def->rotatable){ if (def && def->rotatable){
const std::string& name = def->rotations.name; const std::string& name = def->rotations.name;
@ -311,7 +315,18 @@ void PlayerController::updateInteraction(){
} }
if (Events::jactive(BIND_PLAYER_PICK)){ if (Events::jactive(BIND_PLAYER_PICK)){
Block* block = indices->getBlockDef(chunks->get(x,y,z)->id); Block* block = indices->getBlockDef(chunks->get(x,y,z)->id);
player->setChosenItem(block->rt.pickingItem); itemid_t id = block->rt.pickingItem;
auto inventory = player->getInventory();
size_t slotid = inventory->findSlotByItem(id);
if (slotid == Inventory::npos) {
slotid = player->getChosenSlot();
} else {
player->setChosenSlot(slotid);
}
ItemStack& stack = inventory->getSlot(slotid);
if (stack.getItemId() != id) {
stack.set(ItemStack(id, 1));
}
} }
} else { } else {
selectedBlockId = -1; selectedBlockId = -1;

View File

@ -5,6 +5,7 @@
#include "../world/Level.h" #include "../world/Level.h"
#include "../window/Events.h" #include "../window/Events.h"
#include "../window/Camera.h" #include "../window/Camera.h"
#include "../items/Inventory.h"
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -18,12 +19,16 @@ const float JUMP_FORCE = 8.0f;
Player::Player(glm::vec3 position, float speed) : Player::Player(glm::vec3 position, float speed) :
speed(speed), speed(speed),
chosenItem(0), chosenSlot(0),
camera(new Camera(position, glm::radians(90.0f))), camera(new Camera(position, glm::radians(90.0f))),
spCamera(new Camera(position, glm::radians(90.0f))), spCamera(new Camera(position, glm::radians(90.0f))),
tpCamera(new Camera(position, glm::radians(90.0f))), tpCamera(new Camera(position, glm::radians(90.0f))),
currentCamera(camera), currentCamera(camera),
hitbox(new Hitbox(position, glm::vec3(0.3f,0.9f,0.3f))) { hitbox(new Hitbox(position, glm::vec3(0.3f,0.9f,0.3f))),
inventory(new Inventory(40)) {
}
Player::~Player() {
} }
void Player::update( void Player::update(
@ -118,14 +123,18 @@ void Player::teleport(glm::vec3 position) {
hitbox->position = position; hitbox->position = position;
} }
void Player::setChosenItem(itemid_t id) { void Player::setChosenSlot(int index) {
chosenItem = id; chosenSlot = index;
} }
itemid_t Player::getChosenItem() const { int Player::getChosenSlot() const {
return chosenItem; return chosenSlot;
} }
float Player::getSpeed() const { float Player::getSpeed() const {
return speed; return speed;
} }
std::shared_ptr<Inventory> Player::getInventory() const {
return inventory;
}

View File

@ -9,6 +9,7 @@
class Camera; class Camera;
class Hitbox; class Hitbox;
class Inventory;
class PhysicsSolver; class PhysicsSolver;
class Chunks; class Chunks;
class Level; class Level;
@ -30,11 +31,12 @@ struct PlayerInput {
class Player { class Player {
float speed; float speed;
itemid_t chosenItem; int chosenSlot;
public: public:
std::shared_ptr<Camera> camera, spCamera, tpCamera; std::shared_ptr<Camera> camera, spCamera, tpCamera;
std::shared_ptr<Camera> currentCamera; std::shared_ptr<Camera> currentCamera;
std::unique_ptr<Hitbox> hitbox; std::unique_ptr<Hitbox> hitbox;
std::shared_ptr<Inventory> inventory;
bool flight = false; bool flight = false;
bool noclip = false; bool noclip = false;
bool debug = false; bool debug = false;
@ -43,15 +45,17 @@ public:
glm::vec2 cam = {}; glm::vec2 cam = {};
Player(glm::vec3 position, float speed); Player(glm::vec3 position, float speed);
~Player() = default; ~Player();
void teleport(glm::vec3 position); void teleport(glm::vec3 position);
void update(Level* level, PlayerInput& input, float delta); void update(Level* level, PlayerInput& input, float delta);
void setChosenItem(itemid_t id); void setChosenSlot(int index);
itemid_t getChosenItem() const; int getChosenSlot() const;
float getSpeed() const; float getSpeed() const;
std::shared_ptr<Inventory> getInventory() const;
}; };
#endif /* SRC_OBJECTS_PLAYER_H_ */ #endif /* SRC_OBJECTS_PLAYER_H_ */