mirror of
https://github.com/minetest/minetest.git
synced 2024-09-12 00:26:50 +02:00
Add list-rings
Adds list-rings, a method to implement item sending between inventories via shift-click. Nice insider feature: a ring consisting of a single inventory list serves as nice clean-up method. Also adds them to minimal game, and the standard inventory. Craft output slots are not supported.
This commit is contained in:
parent
3ae8b92be6
commit
c977fbd928
@ -1340,6 +1340,17 @@ examples.
|
|||||||
#### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
|
#### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
|
||||||
* Show an inventory list
|
* Show an inventory list
|
||||||
|
|
||||||
|
#### `listring[<inventory location>;<list name>]`
|
||||||
|
* Allows to create a ring of inventory lists
|
||||||
|
* Shift-clicking on items in one element of the ring
|
||||||
|
* will send them to the next inventory list inside the ring
|
||||||
|
* The first occurrence of an element inside the ring will
|
||||||
|
* determine the inventory where items will be sent to
|
||||||
|
|
||||||
|
#### `listring[]`
|
||||||
|
* Shorthand for doing `listring[<inventory location>;<list name>]`
|
||||||
|
* for the last two inventory lists added by list[...]
|
||||||
|
|
||||||
#### `listcolors[<slot_bg_normal>;<slot_bg_hover>]`
|
#### `listcolors[<slot_bg_normal>;<slot_bg_hover>]`
|
||||||
* Sets background color of slots as `ColorString`
|
* Sets background color of slots as `ColorString`
|
||||||
* Sets background color of slots on mouse hovering
|
* Sets background color of slots on mouse hovering
|
||||||
|
@ -1158,7 +1158,8 @@ minetest.register_node("default:chest", {
|
|||||||
meta:set_string("formspec",
|
meta:set_string("formspec",
|
||||||
"size[8,9]"..
|
"size[8,9]"..
|
||||||
"list[current_name;main;0,0;8,4;]"..
|
"list[current_name;main;0,0;8,4;]"..
|
||||||
"list[current_player;main;0,5;8,4;]")
|
"list[current_player;main;0,5;8,4;]" ..
|
||||||
|
"listring[]")
|
||||||
meta:set_string("infotext", "Chest")
|
meta:set_string("infotext", "Chest")
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
inv:set_size("main", 8*4)
|
inv:set_size("main", 8*4)
|
||||||
@ -1197,7 +1198,8 @@ minetest.register_node("default:chest_locked", {
|
|||||||
meta:set_string("formspec",
|
meta:set_string("formspec",
|
||||||
"size[8,9]"..
|
"size[8,9]"..
|
||||||
"list[current_name;main;0,0;8,4;]"..
|
"list[current_name;main;0,0;8,4;]"..
|
||||||
"list[current_player;main;0,5;8,4;]")
|
"list[current_player;main;0,5;8,4;]" ..
|
||||||
|
"listring[]")
|
||||||
meta:set_string("infotext", "Locked Chest")
|
meta:set_string("infotext", "Locked Chest")
|
||||||
meta:set_string("owner", "")
|
meta:set_string("owner", "")
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
@ -1261,7 +1263,11 @@ default.furnace_inactive_formspec =
|
|||||||
"list[current_name;fuel;2,3;1,1;]"..
|
"list[current_name;fuel;2,3;1,1;]"..
|
||||||
"list[current_name;src;2,1;1,1;]"..
|
"list[current_name;src;2,1;1,1;]"..
|
||||||
"list[current_name;dst;5,1;2,2;]"..
|
"list[current_name;dst;5,1;2,2;]"..
|
||||||
"list[current_player;main;0,5;8,4;]"
|
"list[current_player;main;0,5;8,4;]" ..
|
||||||
|
"listring[current_name;dst]" ..
|
||||||
|
"listring[current_player;main]" ..
|
||||||
|
"listring[current_name;src]" ..
|
||||||
|
"listring[current_player;main]"
|
||||||
|
|
||||||
minetest.register_node("default:furnace", {
|
minetest.register_node("default:furnace", {
|
||||||
description = "Furnace",
|
description = "Furnace",
|
||||||
@ -1398,7 +1404,11 @@ minetest.register_abm({
|
|||||||
"list[current_name;fuel;2,3;1,1;]"..
|
"list[current_name;fuel;2,3;1,1;]"..
|
||||||
"list[current_name;src;2,1;1,1;]"..
|
"list[current_name;src;2,1;1,1;]"..
|
||||||
"list[current_name;dst;5,1;2,2;]"..
|
"list[current_name;dst;5,1;2,2;]"..
|
||||||
"list[current_player;main;0,5;8,4;]")
|
"list[current_player;main;0,5;8,4;]" ..
|
||||||
|
"listring[current_name;dst]" ..
|
||||||
|
"listring[current_player;main]" ..
|
||||||
|
"listring[current_name;src]" ..
|
||||||
|
"listring[current_player;main]")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -352,6 +352,40 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
|
|||||||
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
|
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
|
||||||
|
{
|
||||||
|
if (m_gamedef == 0) {
|
||||||
|
errorstream << "WARNING: invalid use of 'listring' with m_gamedef==0" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> parts = split(element, ';');
|
||||||
|
|
||||||
|
if (parts.size() == 2) {
|
||||||
|
std::string location = parts[0];
|
||||||
|
std::string listname = parts[1];
|
||||||
|
|
||||||
|
InventoryLocation loc;
|
||||||
|
|
||||||
|
if (location == "context" || location == "current_name")
|
||||||
|
loc = m_current_inventory_location;
|
||||||
|
else
|
||||||
|
loc.deSerialize(location);
|
||||||
|
|
||||||
|
m_inventory_rings.push_back(ListRingSpec(loc, listname));
|
||||||
|
return;
|
||||||
|
} else if ((element == "") && (m_inventorylists.size() > 1)) {
|
||||||
|
size_t siz = m_inventorylists.size();
|
||||||
|
// insert the last two inv list elements into the list ring
|
||||||
|
const ListDrawSpec &spa = m_inventorylists[siz - 2];
|
||||||
|
const ListDrawSpec &spb = m_inventorylists[siz - 1];
|
||||||
|
m_inventory_rings.push_back(ListRingSpec(spa.inventoryloc, spa.listname));
|
||||||
|
m_inventory_rings.push_back(ListRingSpec(spb.inventoryloc, spb.listname));
|
||||||
|
}
|
||||||
|
errorstream<< "Invalid list ring element(" << parts.size() << ", "
|
||||||
|
<< m_inventorylists.size() << "): '" << element << "'" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
|
void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
|
||||||
{
|
{
|
||||||
std::vector<std::string> parts = split(element,';');
|
std::vector<std::string> parts = split(element,';');
|
||||||
@ -1661,6 +1695,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == "listring") {
|
||||||
|
parseListRing(data, description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == "checkbox") {
|
if (type == "checkbox") {
|
||||||
parseCheckbox(data,description);
|
parseCheckbox(data,description);
|
||||||
return;
|
return;
|
||||||
@ -3145,6 +3184,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
// from m_selected_item to s.
|
// from m_selected_item to s.
|
||||||
u32 move_amount = 0;
|
u32 move_amount = 0;
|
||||||
|
|
||||||
|
// Set this number to a positive value to generate a move action
|
||||||
|
// from s to the next inventory ring.
|
||||||
|
u32 shift_move_amount = 0;
|
||||||
|
|
||||||
// Set this number to a positive value to generate a drop action
|
// Set this number to a positive value to generate a drop action
|
||||||
// from m_selected_item.
|
// from m_selected_item.
|
||||||
u32 drop_amount = 0;
|
u32 drop_amount = 0;
|
||||||
@ -3166,18 +3209,29 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
else if(m_selected_item == NULL) {
|
else if(m_selected_item == NULL) {
|
||||||
if(s_count != 0) {
|
if(s_count != 0) {
|
||||||
// Non-empty stack has been clicked: select it
|
// Non-empty stack has been clicked: select or shift-move it
|
||||||
m_selected_item = new ItemSpec(s);
|
m_selected_item = new ItemSpec(s);
|
||||||
|
|
||||||
|
u32 count;
|
||||||
if(button == 1) // right
|
if(button == 1) // right
|
||||||
m_selected_amount = (s_count + 1) / 2;
|
count = (s_count + 1) / 2;
|
||||||
else if(button == 2) // middle
|
else if(button == 2) // middle
|
||||||
m_selected_amount = MYMIN(s_count, 10);
|
count = MYMIN(s_count, 10);
|
||||||
else // left
|
else // left
|
||||||
m_selected_amount = s_count;
|
count = s_count;
|
||||||
|
|
||||||
m_selected_dragging = true;
|
if (!event.MouseInput.Shift) {
|
||||||
m_rmouse_auto_place = false;
|
// no shift: select item
|
||||||
|
m_selected_amount = count;
|
||||||
|
m_selected_dragging = true;
|
||||||
|
m_rmouse_auto_place = false;
|
||||||
|
} else {
|
||||||
|
// shift pressed: move item
|
||||||
|
if (button != 1)
|
||||||
|
shift_move_amount = count;
|
||||||
|
else // count of 1 at left click like after drag & drop
|
||||||
|
shift_move_amount = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // m_selected_item != NULL
|
else { // m_selected_item != NULL
|
||||||
@ -3259,8 +3313,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Possibly send inventory action to server
|
// Possibly send inventory action to server
|
||||||
if(move_amount > 0)
|
if (move_amount > 0) {
|
||||||
{
|
|
||||||
// Send IACTION_MOVE
|
// Send IACTION_MOVE
|
||||||
|
|
||||||
assert(m_selected_item && m_selected_item->isValid());
|
assert(m_selected_item && m_selected_item->isValid());
|
||||||
@ -3308,8 +3361,57 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
a->to_list = s.listname;
|
a->to_list = s.listname;
|
||||||
a->to_i = s.i;
|
a->to_i = s.i;
|
||||||
m_invmgr->inventoryAction(a);
|
m_invmgr->inventoryAction(a);
|
||||||
}
|
} else if (shift_move_amount > 0) {
|
||||||
else if(drop_amount > 0) {
|
u32 mis = m_inventory_rings.size();
|
||||||
|
u32 i = 0;
|
||||||
|
for (; i < mis; i++) {
|
||||||
|
const ListRingSpec &sp = m_inventory_rings[i];
|
||||||
|
if (sp.inventoryloc == s.inventoryloc
|
||||||
|
&& sp.listname == s.listname)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if (i >= mis) // if not found
|
||||||
|
break;
|
||||||
|
u32 to_inv_ind = (i + 1) % mis;
|
||||||
|
const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
|
||||||
|
InventoryList *list_from = inv_s->getList(s.listname);
|
||||||
|
if (!list_from)
|
||||||
|
break;
|
||||||
|
Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
|
||||||
|
if (!inv_to)
|
||||||
|
break;
|
||||||
|
InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
|
||||||
|
if (!list_to)
|
||||||
|
break;
|
||||||
|
ItemStack stack_from = list_from->getItem(s.i);
|
||||||
|
assert(shift_move_amount <= stack_from.count);
|
||||||
|
|
||||||
|
// find a place (or more than one) to add the new item
|
||||||
|
u32 slot_to = 0;
|
||||||
|
u32 ilt_size = list_to->getSize();
|
||||||
|
ItemStack leftover;
|
||||||
|
for (; slot_to < ilt_size && shift_move_amount > 0; slot_to++) {
|
||||||
|
list_to->itemFits(slot_to, stack_from, &leftover);
|
||||||
|
if (leftover.count < stack_from.count) {
|
||||||
|
infostream << "Handing IACTION_MOVE to manager" << std::endl;
|
||||||
|
IMoveAction *a = new IMoveAction();
|
||||||
|
a->count = MYMIN(shift_move_amount,
|
||||||
|
(u32) (stack_from.count - leftover.count));
|
||||||
|
shift_move_amount -= a->count;
|
||||||
|
a->from_inv = s.inventoryloc;
|
||||||
|
a->from_list = s.listname;
|
||||||
|
a->from_i = s.i;
|
||||||
|
a->to_inv = to_inv_sp.inventoryloc;
|
||||||
|
a->to_list = to_inv_sp.listname;
|
||||||
|
a->to_i = slot_to;
|
||||||
|
m_invmgr->inventoryAction(a);
|
||||||
|
stack_from = leftover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
} else if (drop_amount > 0) {
|
||||||
m_selected_content_guess = ItemStack(); // Clear
|
m_selected_content_guess = ItemStack(); // Clear
|
||||||
|
|
||||||
// Send IACTION_DROP
|
// Send IACTION_DROP
|
||||||
|
@ -121,6 +121,22 @@ class GUIFormSpecMenu : public GUIModalMenu
|
|||||||
s32 start_item_i;
|
s32 start_item_i;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ListRingSpec
|
||||||
|
{
|
||||||
|
ListRingSpec()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
ListRingSpec(const InventoryLocation &a_inventoryloc,
|
||||||
|
const std::string &a_listname):
|
||||||
|
inventoryloc(a_inventoryloc),
|
||||||
|
listname(a_listname)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryLocation inventoryloc;
|
||||||
|
std::string listname;
|
||||||
|
};
|
||||||
|
|
||||||
struct ImageDrawSpec
|
struct ImageDrawSpec
|
||||||
{
|
{
|
||||||
ImageDrawSpec()
|
ImageDrawSpec()
|
||||||
@ -306,6 +322,7 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
std::vector<ListDrawSpec> m_inventorylists;
|
std::vector<ListDrawSpec> m_inventorylists;
|
||||||
|
std::vector<ListRingSpec> m_inventory_rings;
|
||||||
std::vector<ImageDrawSpec> m_backgrounds;
|
std::vector<ImageDrawSpec> m_backgrounds;
|
||||||
std::vector<ImageDrawSpec> m_images;
|
std::vector<ImageDrawSpec> m_images;
|
||||||
std::vector<ImageDrawSpec> m_itemimages;
|
std::vector<ImageDrawSpec> m_itemimages;
|
||||||
@ -384,6 +401,7 @@ private:
|
|||||||
|
|
||||||
void parseSize(parserData* data,std::string element);
|
void parseSize(parserData* data,std::string element);
|
||||||
void parseList(parserData* data,std::string element);
|
void parseList(parserData* data,std::string element);
|
||||||
|
void parseListRing(parserData* data,std::string element);
|
||||||
void parseCheckbox(parserData* data,std::string element);
|
void parseCheckbox(parserData* data,std::string element);
|
||||||
void parseImage(parserData* data,std::string element);
|
void parseImage(parserData* data,std::string element);
|
||||||
void parseItemImage(parserData* data,std::string element);
|
void parseItemImage(parserData* data,std::string element);
|
||||||
|
@ -71,6 +71,7 @@ Player::Player(IGameDef *gamedef, const char *name):
|
|||||||
//"image[1,0.6;1,2;player.png]"
|
//"image[1,0.6;1,2;player.png]"
|
||||||
"list[current_player;main;0,3.5;8,4;]"
|
"list[current_player;main;0,3.5;8,4;]"
|
||||||
"list[current_player;craft;3,0;3,3;]"
|
"list[current_player;craft;3,0;3,3;]"
|
||||||
|
"listring[]"
|
||||||
"list[current_player;craftpreview;7,1;1,1;]";
|
"list[current_player;craftpreview;7,1;1,1;]";
|
||||||
|
|
||||||
// Initialize movement settings at default values, so movement can work if the server fails to send them
|
// Initialize movement settings at default values, so movement can work if the server fails to send them
|
||||||
|
Loading…
Reference in New Issue
Block a user