VoxelEngine/src/graphics/ui/elements/InventoryView.cpp
Vyacheslav Ivanov 5b325ac2bc
fix: optimization: PVS-Studio warning V815
The 'empty()' method is more efficient for checking if a string is empty compared to comparing it with an empty string literal.

The default constructor 'std::wstring()' is more efficient for creating an empty string compared to using a string literal.

The 'clear()' method is more efficient for clearing a std::wstring compared to assigning it an empty string literal.

Reported by: PVS-Studio
Signed-off-by: Vyacheslav Ivanov <islavaivanov76@gmail.com>
2024-08-02 05:35:55 +03:00

391 lines
11 KiB
C++

#include "InventoryView.hpp"
#include "../../../assets/Assets.hpp"
#include "../../../content/Content.hpp"
#include "../../../frontend/LevelFrontend.hpp"
#include "../../../frontend/locale.hpp"
#include "../../../items/Inventories.hpp"
#include "../../../items/Inventory.hpp"
#include "../../../items/ItemDef.hpp"
#include "../../../items/ItemStack.hpp"
#include "../../../logic/scripting/scripting.hpp"
#include "../../../maths/voxmaths.hpp"
#include "../../../objects/Player.hpp"
#include "../../../util/stringutil.hpp"
#include "../../../voxels/Block.hpp"
#include "../../../window/Events.hpp"
#include "../../../window/input.hpp"
#include "../../../world/Level.hpp"
#include "../../core/Atlas.hpp"
#include "../../core/Batch2D.hpp"
#include "../../core/Font.hpp"
#include "../../core/DrawContext.hpp"
#include "../../core/Shader.hpp"
#include "../../core/Texture.hpp"
#include "../../render/BlocksPreview.hpp"
#include "../GUI.hpp"
#include <glm/glm.hpp>
#include <utility>
using namespace gui;
SlotLayout::SlotLayout(
int index,
glm::vec2 position,
bool background,
bool itemSource,
slotcallback updateFunc,
slotcallback shareFunc,
slotcallback rightClick
) : index(index),
position(position),
background(background),
itemSource(itemSource),
updateFunc(std::move(updateFunc)),
shareFunc(std::move(shareFunc)),
rightClick(std::move(rightClick)) {}
InventoryBuilder::InventoryBuilder() {
view = std::make_shared<InventoryView>();
}
void InventoryBuilder::addGrid(
int cols, int count,
glm::vec2 pos,
int padding,
bool addpanel,
const SlotLayout& slotLayout
) {
const int slotSize = InventoryView::SLOT_SIZE;
const int interval = InventoryView::SLOT_INTERVAL;
int rows = ceildiv(count, cols);
uint width = cols * (slotSize + interval) - interval + padding*2;
uint height = rows * (slotSize + interval) - interval + padding*2;
glm::vec2 vsize = view->getSize();
if (pos.x + width > vsize.x) {
vsize.x = pos.x + width;
}
if (pos.y + height > vsize.y) {
vsize.y = pos.y + height;
}
view->setSize(vsize);
if (addpanel) {
auto panel = std::make_shared<gui::Container>(glm::vec2(width, height));
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f));
view->add(panel, pos);
}
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (row * cols + col >= count) {
break;
}
glm::vec2 position (
col * (slotSize + interval) + padding,
row * (slotSize + interval) + padding
);
auto builtSlot = slotLayout;
builtSlot.index = row * cols + col;
builtSlot.position = position;
add(builtSlot);
}
}
}
void InventoryBuilder::add(const SlotLayout& layout) {
view->add(view->addSlot(layout), layout.position);
}
std::shared_ptr<InventoryView> InventoryBuilder::build() {
return view;
}
SlotView::SlotView(
SlotLayout layout
) : UINode(glm::vec2(InventoryView::SLOT_SIZE)),
layout(std::move(layout))
{
setColor(glm::vec4(0, 0, 0, 0.2f));
setTooltipDelay(0.05f);
}
void SlotView::draw(const DrawContext* pctx, Assets* assets) {
if (bound == nullptr) {
return;
}
itemid_t itemid = bound->getItemId();
if (itemid != prevItem) {
if (itemid) {
auto def = content->getIndices()->items.get(itemid);
tooltip = util::pascal_case(
langs::get(util::str2wstr_utf8(def->caption))
);
} else {
tooltip.clear();
}
}
prevItem = itemid;
const int slotSize = InventoryView::SLOT_SIZE;
const ItemStack& stack = *bound;
glm::vec4 tint(1.0f);
glm::vec2 pos = calcPos();
glm::vec4 color = getColor();
if (hover || highlighted) {
tint *= 1.333f;
color = glm::vec4(1, 1, 1, 0.2f);
}
auto batch = pctx->getBatch2D();
batch->setColor(color);
if (color.a > 0.0) {
batch->texture(nullptr);
if (highlighted) {
batch->rect(pos.x-4, pos.y-4, slotSize+8, slotSize+8);
} else {
batch->rect(pos.x, pos.y, slotSize, slotSize);
}
}
batch->setColor(glm::vec4(1.0f));
auto previews = assets->get<Atlas>("block-previews");
auto indices = content->getIndices();
ItemDef* item = indices->items.get(stack.getItemId());
switch (item->iconType) {
case item_icon_type::none:
break;
case item_icon_type::block: {
const Block& cblock = content->blocks.require(item->icon);
batch->texture(previews->getTexture());
UVRegion region = previews->get(cblock.name);
batch->rect(
pos.x, pos.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
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->get<Texture>(name));
} else {
std::string atlasname = item->icon.substr(0, index);
auto atlas = assets->get<Atlas>(atlasname);
if (atlas && atlas->has(name)) {
region = atlas->get(name);
batch->texture(atlas->getTexture());
}
}
batch->rect(
pos.x, pos.y, slotSize, slotSize,
0, 0, 0, region, false, true, tint);
break;
}
}
if (stack.getCount() > 1) {
auto font = assets->get<Font>("normal");
std::wstring text = std::to_wstring(stack.getCount());
int x = pos.x+slotSize-text.length()*8;
int y = pos.y+slotSize-16;
batch->setColor({0, 0, 0, 1.0f});
font->draw(batch, text, x+1, y+1);
batch->setColor(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, mousecode button) {
if (bound == nullptr)
return;
auto exchangeSlot = std::dynamic_pointer_cast<SlotView>(gui->get(EXCHANGE_SLOT_NAME));
if (exchangeSlot == nullptr) {
return;
}
ItemStack& grabbed = exchangeSlot->getStack();
ItemStack& stack = *bound;
if (button == mousecode::BUTTON_1) {
if (Events::pressed(keycode::LEFT_SHIFT)) {
if (layout.shareFunc) {
layout.shareFunc(layout.index, stack);
}
if (layout.updateFunc) {
layout.updateFunc(layout.index, 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(inventoryid, stack);
if (layout.updateFunc) {
layout.updateFunc(layout.index, stack);
}
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 {
auto stackDef = content->getIndices()->items.get(stack.getItemId());
if (stack.isEmpty()) {
stack.set(grabbed);
stack.setCount(1);
grabbed.setCount(grabbed.getCount()-1);
} else if (stack.accepts(grabbed) && stack.getCount() < stackDef->stackSize){
stack.setCount(stack.getCount()+1);
grabbed.setCount(grabbed.getCount()-1);
}
}
}
if (layout.updateFunc) {
layout.updateFunc(layout.index, stack);
}
}
void SlotView::onFocus(gui::GUI* gui) {
clicked(gui, mousecode::BUTTON_1);
}
const std::wstring& SlotView::getTooltip() const {
const auto& str = UINode::getTooltip();
if (!str.empty() || tooltip.empty()) {
return str;
}
return tooltip;
}
void SlotView::bind(
int64_t inventoryid,
ItemStack& stack,
const Content* content
) {
this->inventoryid = inventoryid;
bound = &stack;
this->content = content;
}
const SlotLayout& SlotView::getLayout() const {
return layout;
}
ItemStack& SlotView::getStack() {
return *bound;
}
InventoryView::InventoryView() : Container(glm::vec2()) {
setColor(glm::vec4(0, 0, 0, 0.0f));
}
InventoryView::~InventoryView() {}
std::shared_ptr<SlotView> InventoryView::addSlot(const SlotLayout& layout) {
uint width = InventoryView::SLOT_SIZE + layout.padding;
uint height = InventoryView::SLOT_SIZE + layout.padding;
auto pos = layout.position;
auto vsize = getSize();
if (pos.x + width > vsize.x) {
vsize.x = pos.x + width;
}
if (pos.y + height > vsize.y) {
vsize.y = pos.y + height;
}
setSize(vsize);
auto slot = std::make_shared<SlotView>(layout);
if (!layout.background) {
slot->setColor(glm::vec4());
}
slots.push_back(slot.get());
return slot;
}
std::shared_ptr<Inventory> InventoryView::getInventory() const {
return inventory;
}
size_t InventoryView::getSlotsCount() const {
return slots.size();
}
void InventoryView::bind(
const std::shared_ptr<Inventory>& inventory,
const Content* content
) {
this->inventory = inventory;
this->content = content;
for (auto slot : slots) {
slot->bind(
inventory->getId(),
inventory->getSlot(slot->getLayout().index),
content
);
}
}
void InventoryView::unbind() {
inventory = nullptr;
}
void InventoryView::setSelected(int index) {
for (size_t i = 0; i < slots.size(); i++) {
slots[i]->setHighlighted(static_cast<int>(i) == index);
}
}
void InventoryView::setPos(glm::vec2 pos) {
Container::setPos(pos - origin);
}
void InventoryView::setOrigin(glm::vec2 origin) {
this->origin = origin;
}
glm::vec2 InventoryView::getOrigin() const {
return origin;
}