commit
1584b07706
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"icon-type": "sprite",
|
"icon-type": "sprite",
|
||||||
"icon": "items:bazalt_breaker"
|
"icon": "items:bazalt_breaker",
|
||||||
|
"uses": 100
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
function util.drop(ppos, itemid, count, pickup_delay)
|
function util.drop(ppos, itemid, count, data, pickup_delay)
|
||||||
if itemid == 0 or not itemid then
|
if itemid == 0 or not itemid then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return entities.spawn("base:drop", ppos, {base__drop={
|
return entities.spawn("base:drop", ppos, {base__drop={
|
||||||
id=itemid,
|
id=itemid,
|
||||||
count=count,
|
count=count,
|
||||||
|
data=data,
|
||||||
pickup_delay=pickup_delay
|
pickup_delay=pickup_delay
|
||||||
}})
|
}})
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
function on_block_break_by(x, y, z, p)
|
function on_block_break_by(x, y, z, pid)
|
||||||
block.set(x, y, z, 0, 0)
|
block.set(x, y, z, 0, 0)
|
||||||
|
if not player.is_infinite_items(pid) then
|
||||||
|
inventory.use(player.get_inventory(pid))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -14,6 +14,7 @@ end
|
|||||||
if SAVED_DATA.item then
|
if SAVED_DATA.item then
|
||||||
dropitem.id = item.index(SAVED_DATA.item)
|
dropitem.id = item.index(SAVED_DATA.item)
|
||||||
dropitem.count = SAVED_DATA.count
|
dropitem.count = SAVED_DATA.count
|
||||||
|
dropitem.data = SAVED_DATA.data
|
||||||
end
|
end
|
||||||
|
|
||||||
local DROP_SCALE = 0.3
|
local DROP_SCALE = 0.3
|
||||||
@ -25,6 +26,7 @@ local rotation = mat4.rotate({
|
|||||||
function on_save()
|
function on_save()
|
||||||
SAVED_DATA.item = item.name(dropitem.id)
|
SAVED_DATA.item = item.name(dropitem.id)
|
||||||
SAVED_DATA.count = dropitem.count
|
SAVED_DATA.count = dropitem.count
|
||||||
|
SAVED_DATA.data = dropitem.data
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- setup visuals
|
do -- setup visuals
|
||||||
@ -59,11 +61,10 @@ function on_sensor_enter(index, oid)
|
|||||||
if pid == -1 then
|
if pid == -1 then
|
||||||
-- other is base:drop too
|
-- other is base:drop too
|
||||||
if index == 0 and other:def_index() == def_index then
|
if index == 0 and other:def_index() == def_index then
|
||||||
local odrop = other:get_component("base:drop")
|
local odrop = other:get_component("base:drop").dropitem
|
||||||
if odrop.dropitem.id == dropitem.id then
|
if odrop.id == dropitem.id and not odrop.data then
|
||||||
-- // TODO: replace combination logic with item.* function
|
|
||||||
local stack = item.stack_size(dropitem.id)
|
local stack = item.stack_size(dropitem.id)
|
||||||
local sum = dropitem.count + odrop.dropitem.count
|
local sum = dropitem.count + odrop.count
|
||||||
if sum <= stack then
|
if sum <= stack then
|
||||||
dropitem.count = sum
|
dropitem.count = sum
|
||||||
other:despawn()
|
other:despawn()
|
||||||
@ -75,7 +76,7 @@ function on_sensor_enter(index, oid)
|
|||||||
|
|
||||||
if timer < 0.0 and index == 0 then
|
if timer < 0.0 and index == 0 then
|
||||||
entity:despawn()
|
entity:despawn()
|
||||||
inventory.add(player.get_inventory(pid), dropitem.id, dropitem.count)
|
inventory.add(player.get_inventory(pid), dropitem.id, dropitem.count, dropitem.data)
|
||||||
audio.play_sound_2d("events/pickup", 0.5, 0.8 + math.random() * 0.4, "regular")
|
audio.play_sound_2d("events/pickup", 0.5, 0.8 + math.random() * 0.4, "regular")
|
||||||
end
|
end
|
||||||
if index == 1 then
|
if index == 1 then
|
||||||
|
|||||||
@ -14,12 +14,13 @@ function on_hud_open()
|
|||||||
if itemid == 0 then
|
if itemid == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
local data = inventory.get_all_data(invid, slot)
|
||||||
inventory.set(invid, slot, itemid, itemcount-1)
|
inventory.set(invid, slot, itemid, itemcount-1)
|
||||||
|
|
||||||
local pvel = {player.get_vel(pid)}
|
local pvel = {player.get_vel(pid)}
|
||||||
local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
|
local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
|
||||||
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
||||||
local drop = base_util.drop(ppos, itemid, 1, 1.5)
|
local drop = base_util.drop(ppos, itemid, 1, data, 1.5)
|
||||||
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
||||||
drop.rigidbody:set_vel(velocity)
|
drop.rigidbody:set_vel(velocity)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@ -94,6 +94,31 @@ elseif __vc_app then
|
|||||||
complete_app_lib(__vc_app)
|
complete_app_lib(__vc_app)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function inventory.get_uses(invid, slot)
|
||||||
|
local uses = inventory.get_data(invid, slot, "uses")
|
||||||
|
if uses == nil then
|
||||||
|
return item.uses(inventory.get(invid, slot))
|
||||||
|
end
|
||||||
|
return uses
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function inventory.use(invid, slot)
|
||||||
|
local itemid, count = inventory.get(invid, slot)
|
||||||
|
if itemid == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local item_uses = inventory.get_uses(invid, slot)
|
||||||
|
if item_uses == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if item_uses == 1 then
|
||||||
|
inventory.set(invid, slot, itemid, count - 1)
|
||||||
|
elseif item_uses > 1 then
|
||||||
|
inventory.set_data(invid, slot, "uses", item_uses - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
------------------- Events ---------------------
|
------------------- Events ---------------------
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|||||||
@ -429,15 +429,29 @@ void ContentLoader::loadItem(
|
|||||||
} else if (iconTypeStr == "sprite") {
|
} else if (iconTypeStr == "sprite") {
|
||||||
def.iconType = ItemIconType::SPRITE;
|
def.iconType = ItemIconType::SPRITE;
|
||||||
} else if (iconTypeStr.length()) {
|
} else if (iconTypeStr.length()) {
|
||||||
logger.error() << name << ": unknown icon type" << iconTypeStr;
|
logger.error() << name << ": unknown icon type - " << iconTypeStr;
|
||||||
}
|
}
|
||||||
root.at("icon").get(def.icon);
|
root.at("icon").get(def.icon);
|
||||||
root.at("placing-block").get(def.placingBlock);
|
root.at("placing-block").get(def.placingBlock);
|
||||||
root.at("script-name").get(def.scriptName);
|
root.at("script-name").get(def.scriptName);
|
||||||
root.at("model-name").get(def.modelName);
|
root.at("model-name").get(def.modelName);
|
||||||
root.at("stack-size").get(def.stackSize);
|
root.at("stack-size").get(def.stackSize);
|
||||||
|
root.at("uses").get(def.uses);
|
||||||
|
|
||||||
|
std::string usesDisplayStr = "";
|
||||||
|
root.at("uses-display").get(usesDisplayStr);
|
||||||
|
if (usesDisplayStr == "none") {
|
||||||
|
def.usesDisplay = ItemUsesDisplay::NONE;
|
||||||
|
} else if (usesDisplayStr == "number") {
|
||||||
|
def.usesDisplay = ItemUsesDisplay::NUMBER;
|
||||||
|
} else if (usesDisplayStr == "relation") {
|
||||||
|
def.usesDisplay = ItemUsesDisplay::RELATION;
|
||||||
|
} else if (usesDisplayStr == "vbar") {
|
||||||
|
def.usesDisplay = ItemUsesDisplay::VBAR;
|
||||||
|
} else if (usesDisplayStr.length()) {
|
||||||
|
logger.error() << name << ": unknown uses display mode - " << usesDisplayStr;
|
||||||
|
}
|
||||||
|
|
||||||
// item light emission [r, g, b] where r,g,b in range [0..15]
|
|
||||||
if (auto found = root.at("emission")) {
|
if (auto found = root.at("emission")) {
|
||||||
const auto& emissionarr = *found;
|
const auto& emissionarr = *found;
|
||||||
def.emission[0] = emissionarr[0].asNumber();
|
def.emission[0] = emissionarr[0].asNumber();
|
||||||
|
|||||||
@ -70,7 +70,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
);
|
);
|
||||||
|
|
||||||
HudElement::HudElement(
|
HudElement::HudElement(
|
||||||
hud_element_mode mode,
|
HudElementMode mode,
|
||||||
UiDocument* document,
|
UiDocument* document,
|
||||||
std::shared_ptr<UINode> node,
|
std::shared_ptr<UINode> node,
|
||||||
bool debug
|
bool debug
|
||||||
@ -83,16 +83,16 @@ void HudElement::update(bool pause, bool inventoryOpen, bool debugMode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case hud_element_mode::permanent:
|
case HudElementMode::PERMANENT:
|
||||||
node->setVisible(true);
|
node->setVisible(true);
|
||||||
break;
|
break;
|
||||||
case hud_element_mode::ingame:
|
case HudElementMode::INGAME:
|
||||||
node->setVisible(!pause && !inventoryOpen);
|
node->setVisible(!pause && !inventoryOpen);
|
||||||
break;
|
break;
|
||||||
case hud_element_mode::inventory_any:
|
case HudElementMode::INVENTORY_ANY:
|
||||||
node->setVisible(inventoryOpen);
|
node->setVisible(inventoryOpen);
|
||||||
break;
|
break;
|
||||||
case hud_element_mode::inventory_bound:
|
case HudElementMode::INVENTORY:
|
||||||
removed = !inventoryOpen;
|
removed = !inventoryOpen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -108,21 +108,21 @@ std::shared_ptr<UINode> HudElement::getNode() const {
|
|||||||
|
|
||||||
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||||
auto& content = frontend.getLevel().content;
|
auto& content = frontend.getLevel().content;
|
||||||
auto indices = content.getIndices();
|
auto& indices = *content.getIndices();
|
||||||
auto inventory = player.getInventory();
|
auto inventory = player.getInventory();
|
||||||
|
|
||||||
size_t itemsCount = indices->items.count();
|
size_t itemsCount = indices.items.count();
|
||||||
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
||||||
for (size_t id = 1; id < itemsCount; id++) {
|
for (size_t id = 1; id < itemsCount; id++) {
|
||||||
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
|
accessInventory->getSlot(id-1).set(ItemStack(id, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotLayout slotLayout(-1, glm::vec2(), false, true, nullptr,
|
SlotLayout slotLayout(-1, glm::vec2(), false, true, nullptr,
|
||||||
[=](uint, ItemStack& item) {
|
[inventory, &indices](uint, ItemStack& item) {
|
||||||
auto copy = ItemStack(item);
|
auto copy = ItemStack(item);
|
||||||
inventory->move(copy, indices);
|
inventory->move(copy, indices);
|
||||||
},
|
},
|
||||||
[=](uint, ItemStack& item) {
|
[this, inventory](uint, ItemStack& item) {
|
||||||
inventory->getSlot(player.getChosenSlot()).set(item);
|
inventory->getSlot(player.getChosenSlot()).set(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||||||
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
|
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
|
||||||
dplotter->setGravity(Gravity::bottom_right);
|
dplotter->setGravity(Gravity::bottom_right);
|
||||||
dplotter->setInteractive(false);
|
dplotter->setInteractive(false);
|
||||||
add(HudElement(hud_element_mode::permanent, nullptr, dplotter, true));
|
add(HudElement(HudElementMode::PERMANENT, nullptr, dplotter, true));
|
||||||
|
|
||||||
assets.store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE);
|
assets.store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE);
|
||||||
|
|
||||||
@ -200,7 +200,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||||||
"<image src='"+DEBUG_WORLDGEN_IMAGE+
|
"<image src='"+DEBUG_WORLDGEN_IMAGE+
|
||||||
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
|
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
|
||||||
);
|
);
|
||||||
add(HudElement(hud_element_mode::permanent, nullptr, debugMinimap, true));
|
add(HudElement(HudElementMode::PERMANENT, nullptr, debugMinimap, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Hud::~Hud() {
|
Hud::~Hud() {
|
||||||
@ -372,8 +372,8 @@ void Hud::openInventory() {
|
|||||||
auto inventoryDocument = assets.get<UiDocument>("core:inventory");
|
auto inventoryDocument = assets.get<UiDocument>("core:inventory");
|
||||||
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
||||||
inventoryView->bind(inventory, &content);
|
inventoryView->bind(inventory, &content);
|
||||||
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
|
add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false));
|
||||||
add(HudElement(hud_element_mode::inventory_bound, nullptr, exchangeSlot, false));
|
add(HudElement(HudElementMode::INVENTORY, nullptr, exchangeSlot, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Inventory> Hud::openInventory(
|
std::shared_ptr<Inventory> Hud::openInventory(
|
||||||
@ -401,7 +401,7 @@ std::shared_ptr<Inventory> Hud::openInventory(
|
|||||||
inv = level.inventories->createVirtual(secondInvView->getSlotsCount());
|
inv = level.inventories->createVirtual(secondInvView->getSlotsCount());
|
||||||
}
|
}
|
||||||
secondInvView->bind(inv, &content);
|
secondInvView->bind(inv, &content);
|
||||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false));
|
add(HudElement(HudElementMode::INVENTORY, doc, secondUI, false));
|
||||||
scripting::on_inventory_open(&player, *inv);
|
scripting::on_inventory_open(&player, *inv);
|
||||||
return inv;
|
return inv;
|
||||||
}
|
}
|
||||||
@ -436,7 +436,7 @@ void Hud::openInventory(
|
|||||||
blockUI->bind(blockinv, &content);
|
blockUI->bind(blockinv, &content);
|
||||||
blockPos = block;
|
blockPos = block;
|
||||||
currentblockid = chunks.require(block.x, block.y, block.z).id;
|
currentblockid = chunks.require(block.x, block.y, block.z).id;
|
||||||
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
|
add(HudElement(HudElementMode::INVENTORY, doc, blockUI, false));
|
||||||
|
|
||||||
scripting::on_inventory_open(&player, *blockinv);
|
scripting::on_inventory_open(&player, *blockinv);
|
||||||
}
|
}
|
||||||
@ -468,8 +468,7 @@ void Hud::showOverlay(
|
|||||||
showExchangeSlot();
|
showExchangeSlot();
|
||||||
inventoryOpen = true;
|
inventoryOpen = true;
|
||||||
}
|
}
|
||||||
add(HudElement(hud_element_mode::inventory_bound, doc, secondUI, false),
|
add(HudElement(HudElementMode::INVENTORY, doc, secondUI, false), args);
|
||||||
args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::openPermanent(UiDocument* doc) {
|
void Hud::openPermanent(UiDocument* doc) {
|
||||||
@ -480,7 +479,7 @@ void Hud::openPermanent(UiDocument* doc) {
|
|||||||
if (invview) {
|
if (invview) {
|
||||||
invview->bind(player.getInventory(), &frontend.getLevel().content);
|
invview->bind(player.getInventory(), &frontend.getLevel().content);
|
||||||
}
|
}
|
||||||
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
add(HudElement(HudElementMode::PERMANENT, doc, doc->getRoot(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::dropExchangeSlot() {
|
void Hud::dropExchangeSlot() {
|
||||||
@ -494,12 +493,12 @@ void Hud::dropExchangeSlot() {
|
|||||||
|
|
||||||
auto indices = frontend.getLevel().content.getIndices();
|
auto indices = frontend.getLevel().content.getIndices();
|
||||||
if (auto invView = std::dynamic_pointer_cast<InventoryView>(blockUI)) {
|
if (auto invView = std::dynamic_pointer_cast<InventoryView>(blockUI)) {
|
||||||
invView->getInventory()->move(stack, indices);
|
invView->getInventory()->move(stack, *indices);
|
||||||
}
|
}
|
||||||
if (stack.isEmpty()) {
|
if (stack.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.getInventory()->move(stack, indices);
|
player.getInventory()->move(stack, *indices);
|
||||||
if (!stack.isEmpty()) {
|
if (!stack.isEmpty()) {
|
||||||
logger.warning() << "discard item [" << stack.getItemId() << ":"
|
logger.warning() << "discard item [" << stack.getItemId() << ":"
|
||||||
<< stack.getCount();
|
<< stack.getCount();
|
||||||
|
|||||||
@ -30,26 +30,26 @@ namespace gui {
|
|||||||
class SlotView;
|
class SlotView;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class hud_element_mode {
|
enum class HudElementMode {
|
||||||
// element is hidden if menu or inventory open
|
// element is hidden if menu or inventory open
|
||||||
ingame,
|
INGAME,
|
||||||
// element is visible if hud is visible
|
// element is visible if hud is visible
|
||||||
permanent,
|
PERMANENT,
|
||||||
// element is visible in inventory mode
|
// element is visible in inventory mode
|
||||||
inventory_any,
|
INVENTORY_ANY,
|
||||||
// element will be removed on inventory close
|
// element will be removed on inventory close
|
||||||
inventory_bound
|
INVENTORY
|
||||||
};
|
};
|
||||||
|
|
||||||
class HudElement {
|
class HudElement {
|
||||||
hud_element_mode mode;
|
HudElementMode mode;
|
||||||
UiDocument* document;
|
UiDocument* document;
|
||||||
std::shared_ptr<gui::UINode> node;
|
std::shared_ptr<gui::UINode> node;
|
||||||
|
|
||||||
bool debug;
|
bool debug;
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
public:
|
public:
|
||||||
HudElement(hud_element_mode mode, UiDocument* document, std::shared_ptr<gui::UINode> node, bool debug);
|
HudElement(HudElementMode mode, UiDocument* document, std::shared_ptr<gui::UINode> node, bool debug);
|
||||||
|
|
||||||
void update(bool pause, bool inventoryOpen, bool debug);
|
void update(bool pause, bool inventoryOpen, bool debug);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public:
|
|||||||
std::shared_ptr<gui::UINode> getNode() const;
|
std::shared_ptr<gui::UINode> getNode() const;
|
||||||
|
|
||||||
bool isInventoryBound() const {
|
bool isInventoryBound() const {
|
||||||
return mode == hud_element_mode::inventory_bound;
|
return mode == HudElementMode::INVENTORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRemoved() {
|
void setRemoved() {
|
||||||
|
|||||||
@ -48,10 +48,15 @@ public:
|
|||||||
void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
|
void sprite(float x, float y, float w, float h, float skew, int atlasRes, int index, glm::vec4 tint);
|
||||||
void point(float x, float y, float r, float g, float b, float a);
|
void point(float x, float y, float r, float g, float b, float a);
|
||||||
|
|
||||||
inline void setColor(glm::vec4 color) {
|
void setColor(glm::vec4 color) {
|
||||||
this->color = color;
|
this->color = color;
|
||||||
}
|
}
|
||||||
inline glm::vec4 getColor() const {
|
|
||||||
|
void resetColor() {
|
||||||
|
this->color = glm::vec4(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec4 getColor() const {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "items/ItemDef.hpp"
|
#include "items/ItemDef.hpp"
|
||||||
#include "items/ItemStack.hpp"
|
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
@ -115,26 +114,73 @@ SlotView::SlotView(
|
|||||||
setTooltipDelay(0.0f);
|
setTooltipDelay(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
|
void SlotView::refreshTooltip(const ItemStack& stack, const ItemDef& item) {
|
||||||
if (bound == nullptr) {
|
itemid_t itemid = stack.getItemId();
|
||||||
|
if (itemid == cache.stack.getItemId()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itemid_t itemid = bound->getItemId();
|
|
||||||
if (itemid != prevItem) {
|
|
||||||
if (itemid) {
|
if (itemid) {
|
||||||
auto& def = content->getIndices()->items.require(itemid);
|
|
||||||
tooltip = util::pascal_case(
|
tooltip = util::pascal_case(
|
||||||
langs::get(util::str2wstr_utf8(def.caption))
|
langs::get(util::str2wstr_utf8(item.caption))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tooltip.clear();
|
tooltip.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlotView::drawItemIcon(
|
||||||
|
Batch2D& batch,
|
||||||
|
const ItemStack& stack,
|
||||||
|
const ItemDef& item,
|
||||||
|
const Assets& assets,
|
||||||
|
const glm::vec4& tint,
|
||||||
|
const glm::vec2& pos
|
||||||
|
) {
|
||||||
|
const int SLOT_SIZE = InventoryView::SLOT_SIZE;
|
||||||
|
const auto& previews = assets.require<Atlas>("block-previews");
|
||||||
|
batch.setColor(glm::vec4(1.0f));
|
||||||
|
switch (item.iconType) {
|
||||||
|
case ItemIconType::NONE:
|
||||||
|
break;
|
||||||
|
case ItemIconType::BLOCK: {
|
||||||
|
const Block& block = content->blocks.require(item.icon);
|
||||||
|
batch.texture(previews.getTexture());
|
||||||
|
|
||||||
|
UVRegion region = previews.get(block.name);
|
||||||
|
batch.rect(
|
||||||
|
pos.x, pos.y, SLOT_SIZE, SLOT_SIZE,
|
||||||
|
0, 0, 0, region, false, true, tint
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
prevItem = itemid;
|
case ItemIconType::SPRITE: {
|
||||||
|
auto textureRegion =
|
||||||
|
util::get_texture_region(assets, item.icon, "blocks:notfound");
|
||||||
|
|
||||||
const int slotSize = InventoryView::SLOT_SIZE;
|
batch.texture(textureRegion.texture);
|
||||||
|
batch.rect(
|
||||||
|
pos.x, pos.y, SLOT_SIZE, SLOT_SIZE,
|
||||||
|
0, 0, 0, textureRegion.region, false, true, tint
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
|
||||||
|
if (bound == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& indices = *content->getIndices();
|
||||||
const ItemStack& stack = *bound;
|
const ItemStack& stack = *bound;
|
||||||
|
const ItemDef& item = indices.items.require(stack.getItemId());
|
||||||
|
|
||||||
|
if (cache.stack.getCount() != stack.getCount()) {
|
||||||
|
cache.countStr = std::to_wstring(stack.getCount());
|
||||||
|
}
|
||||||
|
refreshTooltip(stack, item);
|
||||||
|
cache.stack.set(ItemStack(stack.getItemId(), stack.getCount()));
|
||||||
|
|
||||||
glm::vec4 tint(1, 1, 1, isEnabled() ? 1 : 0.5f);
|
glm::vec4 tint(1, 1, 1, isEnabled() ? 1 : 0.5f);
|
||||||
glm::vec2 pos = calcPos();
|
glm::vec2 pos = calcPos();
|
||||||
glm::vec4 color = getColor();
|
glm::vec4 color = getColor();
|
||||||
@ -144,59 +190,84 @@ void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
|
|||||||
color = glm::vec4(1, 1, 1, 0.2f);
|
color = glm::vec4(1, 1, 1, 0.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto batch = pctx.getBatch2D();
|
auto& batch = *pctx.getBatch2D();
|
||||||
batch->setColor(color);
|
|
||||||
if (color.a > 0.0) {
|
if (color.a > 0.0) {
|
||||||
batch->texture(nullptr);
|
batch.setColor(color);
|
||||||
|
batch.texture(nullptr);
|
||||||
|
|
||||||
|
const int size = InventoryView::SLOT_SIZE;
|
||||||
if (highlighted) {
|
if (highlighted) {
|
||||||
batch->rect(pos.x-4, pos.y-4, slotSize+8, slotSize+8);
|
batch.rect(pos.x - 4, pos.y - 4, size + 8, size + 8);
|
||||||
} else {
|
} else {
|
||||||
batch->rect(pos.x, pos.y, slotSize, slotSize);
|
batch.rect(pos.x, pos.y, size, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
batch->setColor(glm::vec4(1.0f));
|
drawItemIcon(batch, stack, item, assets, tint, pos);
|
||||||
|
|
||||||
auto previews = assets.get<Atlas>("block-previews");
|
if (stack.getCount() > 1 || stack.getFields() != nullptr) {
|
||||||
auto indices = content->getIndices();
|
const auto& font = assets.require<Font>("normal");
|
||||||
|
drawItemInfo(batch, stack, item, font, pos);
|
||||||
auto& item = indices->items.require(stack.getItemId());
|
|
||||||
switch (item.iconType) {
|
|
||||||
case ItemIconType::NONE:
|
|
||||||
break;
|
|
||||||
case ItemIconType::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 ItemIconType::SPRITE: {
|
}
|
||||||
auto textureRegion =
|
|
||||||
util::get_texture_region(assets, item.icon, "blocks:notfound");
|
|
||||||
|
|
||||||
batch->texture(textureRegion.texture);
|
static void draw_shaded_text(
|
||||||
batch->rect(
|
Batch2D& batch, const Font& font, const std::wstring& text, int x, int y
|
||||||
pos.x, pos.y, slotSize, slotSize,
|
) {
|
||||||
0, 0, 0, textureRegion.region, false, true, tint);
|
batch.setColor({0, 0, 0, 1.0f});
|
||||||
break;
|
font.draw(batch, text, x + 1, y + 1, nullptr, 0);
|
||||||
}
|
batch.resetColor();
|
||||||
}
|
font.draw(batch, text, x, y, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlotView::drawItemInfo(
|
||||||
|
Batch2D& batch,
|
||||||
|
const ItemStack& stack,
|
||||||
|
const ItemDef& item,
|
||||||
|
const Font& font,
|
||||||
|
const glm::vec2& pos
|
||||||
|
) {
|
||||||
|
const int SLOT_SIZE = InventoryView::SLOT_SIZE;
|
||||||
if (stack.getCount() > 1) {
|
if (stack.getCount() > 1) {
|
||||||
auto font = assets.get<Font>("normal");
|
const auto& countStr = cache.countStr;
|
||||||
std::wstring text = std::to_wstring(stack.getCount());
|
int x = pos.x + SLOT_SIZE - countStr.length() * 8;
|
||||||
|
int y = pos.y + SLOT_SIZE - 16;
|
||||||
|
draw_shaded_text(batch, font, countStr, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
int x = pos.x+slotSize-text.length()*8;
|
auto usesPtr = stack.getField("uses");
|
||||||
int y = pos.y+slotSize-16;
|
if (usesPtr == nullptr || !usesPtr->isInteger()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int16_t uses = usesPtr->asInteger();
|
||||||
|
if (uses < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (item.usesDisplay) {
|
||||||
|
case ItemUsesDisplay::NONE:
|
||||||
|
break;
|
||||||
|
case ItemUsesDisplay::RELATION:
|
||||||
|
draw_shaded_text(
|
||||||
|
batch, font, std::to_wstring(item.uses), pos.x - 3, pos.y + 9
|
||||||
|
);
|
||||||
|
[[fallthrough]];
|
||||||
|
case ItemUsesDisplay::NUMBER:
|
||||||
|
draw_shaded_text(
|
||||||
|
batch, font, std::to_wstring(uses), pos.x - 3, pos.y - 3
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ItemUsesDisplay::VBAR: {
|
||||||
|
batch.untexture();
|
||||||
|
batch.setColor({0, 0, 0, 0.75f});
|
||||||
|
batch.rect(pos.x - 2, pos.y - 2, 6, SLOT_SIZE + 4);
|
||||||
|
float t = static_cast<float>(uses) / item.uses;
|
||||||
|
|
||||||
batch->setColor({0, 0, 0, 1.0f});
|
int height = SLOT_SIZE * t;
|
||||||
font->draw(*batch, text, x+1, y+1, nullptr, 0);
|
batch.setColor({(1.0f - t * 0.8f), 0.4f, t * 0.8f + 0.2f, 1.0f});
|
||||||
batch->setColor(glm::vec4(1.0f));
|
batch.rect(pos.x, pos.y + SLOT_SIZE - height, 2, height);
|
||||||
font->draw(*batch, text, x, y, nullptr, 0);
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +290,7 @@ void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!layout.itemSource && stack.accepts(grabbed) && layout.placing) {
|
if (!layout.itemSource && stack.accepts(grabbed) && layout.placing) {
|
||||||
stack.move(grabbed, content->getIndices());
|
stack.move(grabbed, *content->getIndices());
|
||||||
} else {
|
} else {
|
||||||
if (layout.itemSource) {
|
if (layout.itemSource) {
|
||||||
if (grabbed.isEmpty()) {
|
if (grabbed.isEmpty()) {
|
||||||
@ -249,10 +320,11 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
|||||||
return;
|
return;
|
||||||
if (grabbed.isEmpty()) {
|
if (grabbed.isEmpty()) {
|
||||||
if (!stack.isEmpty() && layout.taking) {
|
if (!stack.isEmpty() && layout.taking) {
|
||||||
grabbed.set(stack);
|
grabbed.set(std::move(stack));
|
||||||
int halfremain = stack.getCount() / 2;
|
int halfremain = stack.getCount() / 2;
|
||||||
grabbed.setCount(stack.getCount() - halfremain);
|
grabbed.setCount(stack.getCount() - halfremain);
|
||||||
stack.setCount(halfremain);
|
// reset all data in the origin slot
|
||||||
|
stack = ItemStack(stack.getItemId(), halfremain);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -261,9 +333,14 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stack.isEmpty()) {
|
if (stack.isEmpty()) {
|
||||||
stack.set(grabbed);
|
itemcount_t count = grabbed.getCount();
|
||||||
|
stack.set(std::move(grabbed));
|
||||||
stack.setCount(1);
|
stack.setCount(1);
|
||||||
grabbed.setCount(grabbed.getCount() - 1);
|
if (count == 1) {
|
||||||
|
grabbed = {};
|
||||||
|
} else {
|
||||||
|
grabbed = ItemStack(stack.getItemId(), count - 1);
|
||||||
|
}
|
||||||
} else if (stack.accepts(grabbed) && stack.getCount() < stackDef.stackSize) {
|
} else if (stack.accepts(grabbed) && stack.getCount() < stackDef.stackSize) {
|
||||||
stack.setCount(stack.getCount() + 1);
|
stack.setCount(stack.getCount() + 1);
|
||||||
grabbed.setCount(grabbed.getCount() - 1);
|
grabbed.setCount(grabbed.getCount() - 1);
|
||||||
|
|||||||
@ -4,15 +4,18 @@
|
|||||||
#include "Container.hpp"
|
#include "Container.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
#include "items/ItemStack.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class Font;
|
||||||
class Assets;
|
class Assets;
|
||||||
|
class ItemDef;
|
||||||
|
class Batch2D;
|
||||||
class DrawContext;
|
class DrawContext;
|
||||||
class Content;
|
class Content;
|
||||||
class ItemStack;
|
|
||||||
class ContentIndices;
|
class ContentIndices;
|
||||||
class LevelFrontend;
|
class LevelFrontend;
|
||||||
class Inventory;
|
class Inventory;
|
||||||
@ -49,6 +52,11 @@ namespace gui {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class SlotView : public gui::UINode {
|
class SlotView : public gui::UINode {
|
||||||
|
struct {
|
||||||
|
ItemStack stack {};
|
||||||
|
std::wstring countStr;
|
||||||
|
} cache;
|
||||||
|
|
||||||
const Content* content = nullptr;
|
const Content* content = nullptr;
|
||||||
SlotLayout layout;
|
SlotLayout layout;
|
||||||
bool highlighted = false;
|
bool highlighted = false;
|
||||||
@ -56,11 +64,27 @@ namespace gui {
|
|||||||
int64_t inventoryid = 0;
|
int64_t inventoryid = 0;
|
||||||
ItemStack* bound = nullptr;
|
ItemStack* bound = nullptr;
|
||||||
|
|
||||||
std::wstring tooltip;
|
|
||||||
itemid_t prevItem = 0;
|
|
||||||
|
|
||||||
void performLeftClick(ItemStack& stack, ItemStack& grabbed);
|
void performLeftClick(ItemStack& stack, ItemStack& grabbed);
|
||||||
void performRightClick(ItemStack& stack, ItemStack& grabbed);
|
void performRightClick(ItemStack& stack, ItemStack& grabbed);
|
||||||
|
|
||||||
|
void drawItemIcon(
|
||||||
|
Batch2D& batch,
|
||||||
|
const ItemStack& stack,
|
||||||
|
const ItemDef& item,
|
||||||
|
const Assets& assets,
|
||||||
|
const glm::vec4& tint,
|
||||||
|
const glm::vec2& pos
|
||||||
|
);
|
||||||
|
|
||||||
|
void drawItemInfo(
|
||||||
|
Batch2D& batch,
|
||||||
|
const ItemStack& stack,
|
||||||
|
const ItemDef& item,
|
||||||
|
const Font& font,
|
||||||
|
const glm::vec2& pos
|
||||||
|
);
|
||||||
|
|
||||||
|
void refreshTooltip(const ItemStack& stack, const ItemDef& item);
|
||||||
public:
|
public:
|
||||||
SlotView(SlotLayout layout);
|
SlotView(SlotLayout layout);
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ size_t Inventory::findSlotByItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Inventory::move(
|
void Inventory::move(
|
||||||
ItemStack& item, const ContentIndices* indices, size_t begin, size_t end
|
ItemStack& item, const ContentIndices& indices, size_t begin, size_t end
|
||||||
) {
|
) {
|
||||||
end = std::min(slots.size(), end);
|
end = std::min(slots.size(), end);
|
||||||
for (size_t i = begin; i < end && !item.isEmpty(); i++) {
|
for (size_t i = begin; i < end && !item.isEmpty(); i++) {
|
||||||
@ -72,8 +72,12 @@ void Inventory::deserialize(const dv::value& src) {
|
|||||||
if (item.has("count")){
|
if (item.has("count")){
|
||||||
count = item["count"].asInteger();
|
count = item["count"].asInteger();
|
||||||
}
|
}
|
||||||
|
dv::value fields = nullptr;
|
||||||
|
if (item.has("fields")) {
|
||||||
|
fields = item["fields"];
|
||||||
|
}
|
||||||
auto& slot = slots[i];
|
auto& slot = slots[i];
|
||||||
slot.set(ItemStack(id, count));
|
slot.set(ItemStack(id, count, fields));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +96,10 @@ dv::value Inventory::serialize() const {
|
|||||||
if (count) {
|
if (count) {
|
||||||
slotmap["count"] = count;
|
slotmap["count"] = count;
|
||||||
}
|
}
|
||||||
|
const auto& fields = item.getFields();
|
||||||
|
if (fields != nullptr) {
|
||||||
|
slotmap["fields"] = fields;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@ -110,5 +118,3 @@ void Inventory::convert(dv::value& data, const ContentReport* report) {
|
|||||||
inventory.convert(report);
|
inventory.convert(report);
|
||||||
data = inventory.serialize();
|
data = inventory.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t Inventory::npos = -1;
|
|
||||||
|
|||||||
@ -26,13 +26,9 @@ public:
|
|||||||
itemid_t id, size_t begin = 0, size_t end = -1, size_t minCount = 1
|
itemid_t id, size_t begin = 0, size_t end = -1, size_t minCount = 1
|
||||||
);
|
);
|
||||||
|
|
||||||
inline size_t size() const {
|
|
||||||
return slots.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void move(
|
void move(
|
||||||
ItemStack& item,
|
ItemStack& item,
|
||||||
const ContentIndices* indices,
|
const ContentIndices& indices,
|
||||||
size_t begin = 0,
|
size_t begin = 0,
|
||||||
size_t end = -1
|
size_t end = -1
|
||||||
);
|
);
|
||||||
@ -46,17 +42,21 @@ public:
|
|||||||
void convert(const ContentReport* report);
|
void convert(const ContentReport* report);
|
||||||
static void convert(dv::value& data, const ContentReport* report);
|
static void convert(dv::value& data, const ContentReport* report);
|
||||||
|
|
||||||
inline void setId(int64_t id) {
|
size_t size() const {
|
||||||
|
return slots.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setId(int64_t id) {
|
||||||
this->id = id;
|
this->id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int64_t getId() const {
|
int64_t getId() const {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isVirtual() const {
|
bool isVirtual() const {
|
||||||
return id < 0;
|
return id < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t npos;
|
static constexpr size_t npos = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,4 +15,6 @@ void ItemDef::cloneTo(ItemDef& dst) {
|
|||||||
dst.placingBlock = placingBlock;
|
dst.placingBlock = placingBlock;
|
||||||
dst.scriptName = scriptName;
|
dst.scriptName = scriptName;
|
||||||
dst.modelName = modelName;
|
dst.modelName = modelName;
|
||||||
|
dst.uses = uses;
|
||||||
|
dst.usesDisplay = usesDisplay;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,14 @@ enum class ItemIconType {
|
|||||||
BLOCK, // block preview: icon is string block id
|
BLOCK, // block preview: icon is string block id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ItemUsesDisplay {
|
||||||
|
NONE, // uses count is not displayed
|
||||||
|
NUMBER, // uses count is displayed as number
|
||||||
|
RELATION, // uses count is displayed as `remain/default` relation
|
||||||
|
VBAR, // uses count is displayed as vertical bar without counter
|
||||||
|
DEFAULT = VBAR,
|
||||||
|
};
|
||||||
|
|
||||||
struct ItemDef {
|
struct ItemDef {
|
||||||
/// @brief Item string id (with prefix included)
|
/// @brief Item string id (with prefix included)
|
||||||
std::string const name;
|
std::string const name;
|
||||||
@ -28,10 +36,21 @@ struct ItemDef {
|
|||||||
|
|
||||||
dv::value properties = nullptr;
|
dv::value properties = nullptr;
|
||||||
|
|
||||||
|
/// @brief Item max stack size
|
||||||
itemcount_t stackSize = 64;
|
itemcount_t stackSize = 64;
|
||||||
|
|
||||||
|
/// @brief Item is generated for other content unit (like block)
|
||||||
bool generated = false;
|
bool generated = false;
|
||||||
|
|
||||||
|
/// @brief Item light emission [r, g, b] where r,g,b in range [0..15]
|
||||||
uint8_t emission[4] {0, 0, 0, 0};
|
uint8_t emission[4] {0, 0, 0, 0};
|
||||||
|
|
||||||
|
/// @brief Default item uses count
|
||||||
|
int16_t uses = -1;
|
||||||
|
|
||||||
|
/// @brief Item uses count display mode
|
||||||
|
ItemUsesDisplay usesDisplay = ItemUsesDisplay::DEFAULT;
|
||||||
|
|
||||||
ItemIconType iconType = ItemIconType::SPRITE;
|
ItemIconType iconType = ItemIconType::SPRITE;
|
||||||
std::string icon = "blocks:notfound";
|
std::string icon = "blocks:notfound";
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,18 @@
|
|||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "ItemDef.hpp"
|
#include "ItemDef.hpp"
|
||||||
|
|
||||||
ItemStack::ItemStack() : item(ITEM_EMPTY), count(0) {
|
ItemStack::ItemStack(itemid_t item, itemcount_t count, dv::value data)
|
||||||
}
|
: item(item), count(count), fields(std::move(data)) {
|
||||||
|
|
||||||
ItemStack::ItemStack(itemid_t item, itemcount_t count)
|
|
||||||
: item(item), count(count) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemStack::set(const ItemStack& item) {
|
void ItemStack::set(const ItemStack& item) {
|
||||||
|
set(ItemStack(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemStack::set(ItemStack&& item) {
|
||||||
this->item = item.item;
|
this->item = item.item;
|
||||||
this->count = item.count;
|
this->count = item.count;
|
||||||
|
this->fields = std::move(item.fields);
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
this->item = 0;
|
this->item = 0;
|
||||||
}
|
}
|
||||||
@ -25,14 +27,14 @@ bool ItemStack::accepts(const ItemStack& other) const {
|
|||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return item == other.getItemId();
|
return item == other.getItemId() && other.fields == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemStack::move(ItemStack& item, const ContentIndices* indices) {
|
void ItemStack::move(ItemStack& item, const ContentIndices& indices) {
|
||||||
auto& def = indices->items.require(item.getItemId());
|
auto& def = indices.items.require(item.getItemId());
|
||||||
int count = std::min(item.count, def.stackSize - this->count);
|
itemcount_t count = std::min(item.count, def.stackSize - this->count);
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
set(ItemStack(item.getItemId(), count));
|
set(ItemStack(item.getItemId(), count, std::move(item.fields)));
|
||||||
} else {
|
} else {
|
||||||
setCount(this->count + count);
|
setCount(this->count + count);
|
||||||
}
|
}
|
||||||
@ -42,6 +44,30 @@ void ItemStack::move(ItemStack& item, const ContentIndices* indices) {
|
|||||||
void ItemStack::setCount(itemcount_t count) {
|
void ItemStack::setCount(itemcount_t count) {
|
||||||
this->count = count;
|
this->count = count;
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
item = 0;
|
clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemStack::setField(std::string_view name, dv::value value) {
|
||||||
|
if (fields == nullptr) {
|
||||||
|
if (value == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fields = dv::object();
|
||||||
|
}
|
||||||
|
if (value == nullptr) {
|
||||||
|
fields.erase(std::string(name));
|
||||||
|
if (fields.empty()) {
|
||||||
|
fields = nullptr;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fields[std::string(name)] = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value* ItemStack::getField(const std::string& name) const {
|
||||||
|
if (fields == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return fields.at(name).ptr;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,36 +2,60 @@
|
|||||||
|
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "data/dv.hpp"
|
||||||
|
|
||||||
class ContentIndices;
|
class ContentIndices;
|
||||||
|
|
||||||
class ItemStack {
|
class ItemStack {
|
||||||
itemid_t item;
|
itemid_t item = ITEM_EMPTY;
|
||||||
itemcount_t count;
|
itemcount_t count = 0;
|
||||||
|
dv::value fields = nullptr;
|
||||||
public:
|
public:
|
||||||
ItemStack();
|
ItemStack() = default;
|
||||||
|
|
||||||
ItemStack(itemid_t item, itemcount_t count);
|
ItemStack(itemid_t item, itemcount_t count, dv::value data=nullptr);
|
||||||
|
|
||||||
void set(const ItemStack& item);
|
void set(const ItemStack& item);
|
||||||
|
void set(ItemStack&& item);
|
||||||
void setCount(itemcount_t count);
|
void setCount(itemcount_t count);
|
||||||
|
|
||||||
bool accepts(const ItemStack& item) const;
|
/// @brief Set a field in the item stack data.
|
||||||
void move(ItemStack& item, const ContentIndices* indices);
|
void setField(std::string_view name, dv::value value);
|
||||||
|
|
||||||
inline void clear() {
|
/// @brief Get a field from the item stack data.
|
||||||
|
/// @param name field name
|
||||||
|
/// @return value pointer or nullptr if the field does not exist.
|
||||||
|
dv::value* getField(const std::string& name) const;
|
||||||
|
|
||||||
|
bool accepts(const ItemStack& item) const;
|
||||||
|
|
||||||
|
/// @brief Move items from one stack to another.
|
||||||
|
/// If the target stack is completely filled, the source stack will be reduced.
|
||||||
|
/// @param item source stack
|
||||||
|
/// @param indices content indices
|
||||||
|
void move(ItemStack& item, const ContentIndices& indices);
|
||||||
|
|
||||||
|
void clear() {
|
||||||
set(ItemStack(0, 0));
|
set(ItemStack(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isEmpty() const {
|
bool isEmpty() const {
|
||||||
return item == ITEM_EMPTY;
|
return item == ITEM_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline itemid_t getItemId() const {
|
itemid_t getItemId() const {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline itemcount_t getCount() const {
|
itemcount_t getCount() const {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dv::value& getFields() const {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasFields() const {
|
||||||
|
return fields != nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,21 +7,22 @@
|
|||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static void validate_itemid(itemid_t id) {
|
namespace {
|
||||||
|
void validate_itemid(itemid_t id) {
|
||||||
if (id >= indices->items.count()) {
|
if (id >= indices->items.count()) {
|
||||||
throw std::runtime_error("invalid item id");
|
throw std::runtime_error("invalid item id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Inventory& get_inventory(int64_t id) {
|
Inventory& get_inventory(int64_t id) {
|
||||||
auto inv = level->inventories->get(id);
|
auto inv = level->inventories->get(id);
|
||||||
if (inv == nullptr) {
|
if (inv == nullptr) {
|
||||||
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
throw std::runtime_error("inventory not found: " + std::to_string(id));
|
||||||
}
|
}
|
||||||
return *inv;
|
return *inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Inventory& get_inventory(int64_t id, int arg) {
|
Inventory& get_inventory(int64_t id, int arg) {
|
||||||
auto inv = level->inventories->get(id);
|
auto inv = level->inventories->get(id);
|
||||||
if (inv == nullptr) {
|
if (inv == nullptr) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@ -30,45 +31,49 @@ static Inventory& get_inventory(int64_t id, int arg) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return *inv;
|
return *inv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void validate_slotid(int slotid, const Inventory& inv) {
|
void validate_slotid(int slotid, const Inventory& inv) {
|
||||||
if (static_cast<size_t>(slotid) >= inv.size()) {
|
if (static_cast<size_t>(slotid) >= inv.size()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"slot index is out of range [0..inventory.size(invid)]"
|
"slot index is out of range [0..inventory.size(invid)]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get(lua::State* L) {
|
using SlotFunc = int(lua::State*, ItemStack&);
|
||||||
|
|
||||||
|
template <SlotFunc func>
|
||||||
|
int wrap_slot(lua::State* L) {
|
||||||
auto invid = lua::tointeger(L, 1);
|
auto invid = lua::tointeger(L, 1);
|
||||||
auto slotid = lua::tointeger(L, 2);
|
auto slotid = lua::tointeger(L, 2);
|
||||||
auto inv = get_inventory(invid);
|
auto& inv = get_inventory(invid);
|
||||||
validate_slotid(slotid, inv);
|
validate_slotid(slotid, inv);
|
||||||
const ItemStack& item = inv.getSlot(slotid);
|
auto& item = inv.getSlot(slotid);
|
||||||
|
return func(L, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get(lua::State* L, ItemStack& item) {
|
||||||
lua::pushinteger(L, item.getItemId());
|
lua::pushinteger(L, item.getItemId());
|
||||||
lua::pushinteger(L, item.getCount());
|
lua::pushinteger(L, item.getCount());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set(lua::State* L) {
|
static int l_set(lua::State* L, ItemStack& item) {
|
||||||
auto invid = lua::tointeger(L, 1);
|
|
||||||
auto slotid = lua::tointeger(L, 2);
|
|
||||||
auto itemid = lua::tointeger(L, 3);
|
auto itemid = lua::tointeger(L, 3);
|
||||||
auto count = lua::tointeger(L, 4);
|
auto count = lua::tointeger(L, 4);
|
||||||
validate_itemid(itemid);
|
auto data = lua::tovalue(L, 5);
|
||||||
|
if (!data.isObject() && data != nullptr) {
|
||||||
auto& inv = get_inventory(invid);
|
throw std::runtime_error("invalid data argument type (table expected)");
|
||||||
|
}
|
||||||
validate_slotid(slotid, inv);
|
item.set(ItemStack(itemid, count, std::move(data)));
|
||||||
ItemStack& item = inv.getSlot(slotid);
|
|
||||||
item.set(ItemStack(itemid, count));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_size(lua::State* L) {
|
static int l_size(lua::State* L) {
|
||||||
auto invid = lua::tointeger(L, 1);
|
auto invid = lua::tointeger(L, 1);
|
||||||
auto& inv = get_inventory(invid);
|
const auto& inv = get_inventory(invid);
|
||||||
return lua::pushinteger(L, inv.size());
|
return lua::pushinteger(L, inv.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,11 +81,16 @@ static int l_add(lua::State* L) {
|
|||||||
auto invid = lua::tointeger(L, 1);
|
auto invid = lua::tointeger(L, 1);
|
||||||
auto itemid = lua::tointeger(L, 2);
|
auto itemid = lua::tointeger(L, 2);
|
||||||
auto count = lua::tointeger(L, 3);
|
auto count = lua::tointeger(L, 3);
|
||||||
|
auto data = lua::tovalue(L, 4);
|
||||||
|
|
||||||
validate_itemid(itemid);
|
validate_itemid(itemid);
|
||||||
|
if (!data.isObject() && data != nullptr) {
|
||||||
|
throw std::runtime_error("invalid data argument type (table expected)");
|
||||||
|
}
|
||||||
|
|
||||||
auto& inv = get_inventory(invid);
|
auto& inv = get_inventory(invid);
|
||||||
ItemStack item(itemid, count);
|
ItemStack item(itemid, count, std::move(data));
|
||||||
inv.move(item, indices);
|
inv.move(item, *indices);
|
||||||
return lua::pushinteger(L, item.getCount());
|
return lua::pushinteger(L, item.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,9 +154,9 @@ static int l_move(lua::State* L) {
|
|||||||
auto& invB = get_inventory(invBid, 3);
|
auto& invB = get_inventory(invBid, 3);
|
||||||
auto& slot = invA.getSlot(slotAid);
|
auto& slot = invA.getSlot(slotAid);
|
||||||
if (slotBid == -1) {
|
if (slotBid == -1) {
|
||||||
invB.move(slot, content->getIndices());
|
invB.move(slot, *content->getIndices());
|
||||||
} else {
|
} else {
|
||||||
invB.move(slot, content->getIndices(), slotBid, slotBid + 1);
|
invB.move(slot, *content->getIndices(), slotBid, slotBid + 1);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -163,9 +173,9 @@ static int l_move_range(lua::State* L) {
|
|||||||
auto invB = get_inventory(invBid, 3);
|
auto invB = get_inventory(invBid, 3);
|
||||||
auto& slot = invA.getSlot(slotAid);
|
auto& slot = invA.getSlot(slotAid);
|
||||||
if (slotBegin == -1) {
|
if (slotBegin == -1) {
|
||||||
invB.move(slot, content->getIndices());
|
invB.move(slot, *content->getIndices());
|
||||||
} else {
|
} else {
|
||||||
invB.move(slot, content->getIndices(), slotBegin, slotEnd);
|
invB.move(slot, *content->getIndices(), slotBegin, slotEnd);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -184,9 +194,38 @@ static int l_find_by_item(lua::State* L) {
|
|||||||
return lua::pushinteger(L, index);
|
return lua::pushinteger(L, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_get_data(lua::State* L, ItemStack& stack) {
|
||||||
|
auto key = lua::require_string(L, 3);
|
||||||
|
auto value = stack.getField(key);
|
||||||
|
if (value == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return lua::pushvalue(L, *value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_all_data(lua::State* L, ItemStack& stack) {
|
||||||
|
return lua::pushvalue(L, stack.getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_has_data(lua::State* L, ItemStack& stack) {
|
||||||
|
auto key = lua::tostring(L, 3);
|
||||||
|
if (key == nullptr) {
|
||||||
|
return lua::pushboolean(L, stack.hasFields());
|
||||||
|
}
|
||||||
|
return lua::pushboolean(L, stack.getField(key) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_data(lua::State* L, ItemStack& stack) {
|
||||||
|
auto key = lua::require_string(L, 3);
|
||||||
|
auto value = lua::tovalue(L, 4);
|
||||||
|
auto& fields = stack.getFields();
|
||||||
|
stack.setField(key, std::move(value));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg inventorylib[] = {
|
const luaL_Reg inventorylib[] = {
|
||||||
{"get", lua::wrap<l_get>},
|
{"get", wrap_slot<l_get>},
|
||||||
{"set", lua::wrap<l_set>},
|
{"set", wrap_slot<l_set>},
|
||||||
{"size", lua::wrap<l_size>},
|
{"size", lua::wrap<l_size>},
|
||||||
{"add", lua::wrap<l_add>},
|
{"add", lua::wrap<l_add>},
|
||||||
{"move", lua::wrap<l_move>},
|
{"move", lua::wrap<l_move>},
|
||||||
@ -195,7 +234,12 @@ const luaL_Reg inventorylib[] = {
|
|||||||
{"get_block", lua::wrap<l_get_block>},
|
{"get_block", lua::wrap<l_get_block>},
|
||||||
{"bind_block", lua::wrap<l_bind_block>},
|
{"bind_block", lua::wrap<l_bind_block>},
|
||||||
{"unbind_block", lua::wrap<l_unbind_block>},
|
{"unbind_block", lua::wrap<l_unbind_block>},
|
||||||
|
{"get_data", wrap_slot<l_get_data>},
|
||||||
|
{"set_data", wrap_slot<l_set_data>},
|
||||||
|
{"get_all_data", wrap_slot<l_get_all_data>},
|
||||||
|
{"has_data", wrap_slot<l_has_data>},
|
||||||
{"create", lua::wrap<l_create>},
|
{"create", lua::wrap<l_create>},
|
||||||
{"remove", lua::wrap<l_remove>},
|
{"remove", lua::wrap<l_remove>},
|
||||||
{"clone", lua::wrap<l_clone>},
|
{"clone", lua::wrap<l_clone>},
|
||||||
{NULL, NULL}};
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|||||||
@ -80,6 +80,13 @@ static int l_emission(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_uses(lua::State* L) {
|
||||||
|
if (auto def = get_item_def(L, 1)) {
|
||||||
|
return lua::pushinteger(L, def->uses);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg itemlib[] = {
|
const luaL_Reg itemlib[] = {
|
||||||
{"index", lua::wrap<l_index>},
|
{"index", lua::wrap<l_index>},
|
||||||
{"name", lua::wrap<l_name>},
|
{"name", lua::wrap<l_name>},
|
||||||
@ -90,4 +97,6 @@ const luaL_Reg itemlib[] = {
|
|||||||
{"placing_block", lua::wrap<l_placing_block>},
|
{"placing_block", lua::wrap<l_placing_block>},
|
||||||
{"model_name", lua::wrap<l_model_name>},
|
{"model_name", lua::wrap<l_model_name>},
|
||||||
{"emission", lua::wrap<l_emission>},
|
{"emission", lua::wrap<l_emission>},
|
||||||
{NULL, NULL}};
|
{"uses", lua::wrap<l_uses>},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user