cg_plus removed, craftguide added
|
@ -1,165 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
|
@ -1,39 +0,0 @@
|
|||
# Crafting Guide Plus
|
||||
|
||||
[![ContentDB](https://content.minetest.net/packages/random_geek/cg_plus/shields/downloads/)](https://content.minetest.net/packages/random_geek/cg_plus/)
|
||||
|
||||
![screenshot](screenshot.png)
|
||||
|
||||
Crafting Guide Plus is a simple and intuitive crafting guide and auto-crafting mod for Minetest. CGP is compatible with
|
||||
Minetest Game and any other games that use sfinv. It was built mostly from the ground up, with some inspiration from
|
||||
jp's Crafting Guide, Unified Inventory, and Minetest Game's builtin crafting guide.
|
||||
|
||||
Note: If `mtg_craftguide` is present, CGP will override its page in the inventory. You may want to remove
|
||||
`mtg_craftguide` entirely for optimization purposes.
|
||||
|
||||
## Features:
|
||||
|
||||
- "Intelligent" auto-crafting, or rather, automatic craft staging. This feature can be disabled if it is not wanted.
|
||||
- Group support, including group search and support for craft recipes requiring items in multiple groups.
|
||||
- Shaped and shapeless crafting recipe previews of any size.
|
||||
- Fuel and cooking recipes, including fuel replacements and burning/cooking times.
|
||||
- Digging and digging by chance (item drop) previews.
|
||||
|
||||
## Known issues:
|
||||
|
||||
- The auto-crafting algorithm is not *perfect*. For craft recipes requiring items in a group, only the item with the
|
||||
greatest count from the player's inventory will be utilized.
|
||||
- Items in multiple groups will not always display correctly in craft view.
|
||||
|
||||
## License
|
||||
|
||||
Code is licensed under the GNU LGPL v3.0. Images and other media are CC BY-SA 4.0 unless otherwise noted.
|
||||
|
||||
The following images are from Minetest Game, and their respective licenses apply:
|
||||
|
||||
```
|
||||
cg_plus_icon_autocrafting.png Adapted from default_tool_stonepick.png
|
||||
cg_plus_icon_cooking.png From default_furnace_front_active.png
|
||||
cg_plus_icon_digging.png From default_tool_stonepick.png
|
||||
cg_plus_icon_fuel.png From default_furnace_fire_fg.png
|
||||
```
|
|
@ -1,264 +0,0 @@
|
|||
-- TODO: aliases?
|
||||
|
||||
local function get_drops(item, def)
|
||||
local normalDrops = {}
|
||||
local randomDrops = {}
|
||||
|
||||
if type(def.drop) == "table" then
|
||||
-- Handle complex drops. This is the method used by Unified Inventory.
|
||||
local maxStart = true
|
||||
local itemsLeft = def.drop.max_items
|
||||
local dropTables = def.drop.items or {}
|
||||
|
||||
local dStack, dName, dCount
|
||||
|
||||
for _, dropTable in ipairs(dropTables) do
|
||||
if itemsLeft and itemsLeft <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
for _, dropItem in ipairs(dropTable.items) do
|
||||
dStack = ItemStack(dropItem)
|
||||
dName = dStack:get_name()
|
||||
dCount = dStack:get_count()
|
||||
|
||||
if dCount > 0 and dName ~= item then
|
||||
if #dropTable.items == 1 and dropTable.rarity == 1
|
||||
and maxStart then
|
||||
normalDrops[dName] = (normalDrops[dName] or 0) + dCount
|
||||
|
||||
if itemsLeft then
|
||||
itemsLeft = itemsLeft - 1
|
||||
if itemsLeft <= 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
if itemsLeft then
|
||||
maxStart = false
|
||||
end
|
||||
|
||||
randomDrops[dName] = (randomDrops[dName] or 0) + dCount
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Handle simple, one-item drops.
|
||||
local dStack = ItemStack(def.drop)
|
||||
|
||||
if not dStack:is_empty() and dStack:get_name() ~= item then
|
||||
normalDrops[dStack:get_name()] = dStack:get_count()
|
||||
end
|
||||
end
|
||||
|
||||
return normalDrops, randomDrops
|
||||
end
|
||||
|
||||
function cg.build_item_list()
|
||||
local startTime = minetest.get_us_time()
|
||||
cg.items_all.list = {}
|
||||
|
||||
for item, def in pairs(minetest.registered_items) do
|
||||
if def.description and def.description ~= ""
|
||||
and minetest.get_item_group(item,
|
||||
"not_in_creative_inventory") == 0
|
||||
and minetest.get_item_group(item,
|
||||
"not_in_craft_guide") == 0 then
|
||||
table.insert(cg.items_all.list, item)
|
||||
cg.crafts[item] = minetest.get_all_craft_recipes(item) or {}
|
||||
end
|
||||
end
|
||||
|
||||
local def, fuel, decremented
|
||||
|
||||
for _, item in ipairs(cg.items_all.list) do
|
||||
def = minetest.registered_items[item]
|
||||
|
||||
fuel, decremented = minetest.get_craft_result({
|
||||
method = "fuel",
|
||||
width = 0,
|
||||
items = {ItemStack(item)}
|
||||
})
|
||||
|
||||
if fuel.time > 0 then
|
||||
table.insert(cg.crafts[item], {
|
||||
type = "fuel",
|
||||
items = {item},
|
||||
output = decremented.items[1]:to_string(),
|
||||
time = fuel.time,
|
||||
})
|
||||
end
|
||||
|
||||
if def.drop then
|
||||
local normalDrops, randomDrops = get_drops(item, def)
|
||||
|
||||
for dItem, dCount in pairs(normalDrops) do
|
||||
if cg.crafts[dItem] then
|
||||
table.insert(cg.crafts[dItem], {
|
||||
type = "digging",
|
||||
width = 0,
|
||||
items = {item},
|
||||
output = ItemStack({
|
||||
name = dItem,
|
||||
count = dCount
|
||||
}):to_string()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for dItem, dCount in pairs(randomDrops) do
|
||||
if cg.crafts[dItem] then
|
||||
table.insert(cg.crafts[dItem], {
|
||||
type = "digging_chance",
|
||||
width = 0,
|
||||
items = {item},
|
||||
output = ItemStack({
|
||||
name = dItem,
|
||||
count = dCount
|
||||
}):to_string()
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for group, _ in pairs(def.groups) do
|
||||
if not cg.group_stereotypes[group] then
|
||||
cg.group_stereotypes[group] = item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(cg.items_all.list)
|
||||
cg.items_all.num_pages = math.ceil(#cg.items_all.list / cg.PAGE_ITEMS)
|
||||
|
||||
minetest.log("info",
|
||||
string.format(
|
||||
"[cg_plus] Finished building item list in %.3f s.",
|
||||
(minetest.get_us_time() - startTime) / 1000000
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
function cg.filter_items(player, filter)
|
||||
local playerName = player:get_player_name()
|
||||
local playerData = cg.player_data[playerName]
|
||||
|
||||
if not filter or filter == "" then
|
||||
playerData.items = nil
|
||||
return
|
||||
end
|
||||
|
||||
playerData.items = {list = {}}
|
||||
|
||||
filter = filter:lower()
|
||||
local groupFilter = string.sub(filter, 1, 6) == "group:" and filter:sub(7)
|
||||
|
||||
if groupFilter and cg.GROUP_SEARCH then
|
||||
-- Search by group
|
||||
local groups = string.split(groupFilter, ",")
|
||||
local isInGroups
|
||||
|
||||
for _, item in ipairs(cg.items_all.list) do
|
||||
isInGroups = true
|
||||
|
||||
for idx = 1, math.min(#groups, cg.GROUP_SEARCH_MAX) do
|
||||
if minetest.get_item_group(item, groups[idx]) == 0 then
|
||||
isInGroups = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if isInGroups then
|
||||
table.insert(playerData.items.list, item)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Regular search
|
||||
local langCode = playerData.lang_code
|
||||
|
||||
for _, item in ipairs(cg.items_all.list) do
|
||||
if item:lower():find(filter, 1, true)
|
||||
or minetest.get_translated_string(langCode,
|
||||
minetest.registered_items[item].description)
|
||||
:lower():find(filter, 1, true) then
|
||||
table.insert(playerData.items.list, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
playerData.items.num_pages =
|
||||
math.ceil(#cg.get_item_list(player).list / cg.PAGE_ITEMS)
|
||||
end
|
||||
|
||||
function cg.parse_craft(craft)
|
||||
local type = craft.type
|
||||
local template = cg.craft_types[type] or {}
|
||||
|
||||
if craft.width == 0 and template.alt_zero_width then
|
||||
type = template.alt_zero_width
|
||||
template = cg.craft_types[template.alt_zero_width] or {}
|
||||
end
|
||||
|
||||
local newCraft = {
|
||||
type = type,
|
||||
items = {},
|
||||
output = craft.output,
|
||||
}
|
||||
|
||||
if template.get_infotext then
|
||||
newCraft.infotext = template.get_infotext(craft) or ""
|
||||
end
|
||||
|
||||
local width = math.max(craft.width or 0, 1)
|
||||
|
||||
if template.get_grid_size then
|
||||
newCraft.grid_size = template.get_grid_size(craft)
|
||||
else
|
||||
newCraft.grid_size = {
|
||||
x = width,
|
||||
y = math.ceil(table.maxn(craft.items) / width)
|
||||
}
|
||||
end
|
||||
|
||||
if template.inherit_width then
|
||||
-- For shapeless recipes, there is no need to modify the item list.
|
||||
newCraft.items = craft.items
|
||||
else
|
||||
-- The craft's width is not always the same as the grid size, so items
|
||||
-- need to be shifted around.
|
||||
for idx, item in pairs(craft.items) do
|
||||
newCraft.items[idx + (newCraft.grid_size.x - width) *
|
||||
math.floor((idx - 1) / width)] = item
|
||||
end
|
||||
end
|
||||
|
||||
return newCraft
|
||||
end
|
||||
|
||||
function cg.get_item_list(player)
|
||||
return cg.player_data[player:get_player_name()].items or cg.items_all
|
||||
end
|
||||
|
||||
function cg.register_craft_type(name, def)
|
||||
cg.craft_types[name] = def
|
||||
end
|
||||
|
||||
function cg.register_group_stereotype(group, item)
|
||||
cg.group_stereotypes[group] = item
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(cg.build_item_list)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local playerName = player:get_player_name()
|
||||
local langCode = minetest.get_player_information(playerName).lang_code
|
||||
|
||||
cg.player_data[playerName] = {
|
||||
lang_code = langCode
|
||||
}
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player, timed_out)
|
||||
cg.player_data[player:get_player_name()] = nil
|
||||
end)
|
|
@ -1,168 +0,0 @@
|
|||
local function add_or_create(t, i, n)
|
||||
t[i] = t[i] and t[i] + n or n
|
||||
end
|
||||
|
||||
local function get_group_item(invCache, groups)
|
||||
local maxCount = 0
|
||||
local maxItem
|
||||
local isInGroups
|
||||
|
||||
for item, count in pairs(invCache) do
|
||||
isInGroups = true
|
||||
|
||||
for _, group in ipairs(groups) do
|
||||
if minetest.get_item_group(item, group) == 0 then
|
||||
isInGroups = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if isInGroups and count > maxCount then
|
||||
maxItem = item
|
||||
maxCount = count
|
||||
end
|
||||
end
|
||||
|
||||
return maxItem
|
||||
end
|
||||
|
||||
function cg.auto_get_craftable(player, craft)
|
||||
local inv = player:get_inventory():get_list("main")
|
||||
local invCache = {}
|
||||
|
||||
-- Create a cache of the inventory with itemName = count pairs.
|
||||
-- This speeds up searching for items.
|
||||
for _, stack in ipairs(inv) do
|
||||
if stack:get_count() > 0 then
|
||||
add_or_create(invCache, stack:get_name(), stack:get_count())
|
||||
end
|
||||
end
|
||||
|
||||
local reqItems = {}
|
||||
local reqGroups = {}
|
||||
|
||||
-- Find out how many of each item/group is required to craft one item.
|
||||
for _, item in pairs(craft.items) do
|
||||
if item:sub(1, 6) == "group:" then
|
||||
add_or_create(reqGroups, item, 1)
|
||||
else
|
||||
add_or_create(reqItems, item, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local gMaxItem
|
||||
|
||||
-- For each group, find the item in that group from the player's inventory
|
||||
-- with the largest count.
|
||||
for group, count in pairs(reqGroups) do
|
||||
gMaxItem = get_group_item(invCache, group:sub(7):split(","))
|
||||
|
||||
if gMaxItem then
|
||||
add_or_create(reqItems, gMaxItem, count)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local craftable = 1000
|
||||
|
||||
for item, count in pairs(reqItems) do
|
||||
if invCache[item] then
|
||||
craftable = math.min(craftable, math.floor(invCache[item] / count))
|
||||
else
|
||||
return 0
|
||||
end
|
||||
|
||||
-- We can't craft more than the stack_max of our ingredients.
|
||||
if minetest.registered_items[item].stack_max then
|
||||
craftable = math.min(craftable,
|
||||
minetest.registered_items[item].stack_max)
|
||||
end
|
||||
end
|
||||
|
||||
return craftable
|
||||
end
|
||||
|
||||
function cg.auto_craft(player, craft, num)
|
||||
local inv = player:get_inventory()
|
||||
|
||||
if not inv:is_empty("craft") then
|
||||
-- Attempt to move items to the player's main inventory.
|
||||
for idx, stack in ipairs(inv:get_list("craft")) do
|
||||
if not stack:is_empty() then
|
||||
stack = inv:add_item("main", stack)
|
||||
inv:set_stack("craft", idx, stack)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check again, and return if not all items were moved.
|
||||
if not inv:is_empty("craft") then
|
||||
minetest.chat_send_player(player:get_player_name(),
|
||||
cg.S("Item could not be crafted!"))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if craft.width > inv:get_width("craft")
|
||||
or table.maxn(craft.items) > inv:get_size("craft") then
|
||||
return
|
||||
end
|
||||
|
||||
local invList = inv:get_list("main")
|
||||
local width = craft.width == 0 and inv:get_width("craft") or craft.width
|
||||
local stack, invCache
|
||||
local groupCache = {}
|
||||
|
||||
for idx, item in pairs(craft.items) do
|
||||
-- Shift the indices so the items in the craft go to the right spots on
|
||||
-- the crafting grid.
|
||||
idx = (idx + (inv:get_width("craft") - width) *
|
||||
math.floor((idx - 1) / width))
|
||||
|
||||
if item:sub(1, 6) == "group:" then
|
||||
-- Create an inventory cache.
|
||||
if not invCache then
|
||||
invCache = {}
|
||||
|
||||
for _, stack in ipairs(invList) do
|
||||
if stack:get_count() > 0 then
|
||||
add_or_create(invCache, stack:get_name(),
|
||||
stack:get_count())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the most plentiful item in the group.
|
||||
if not groupCache[item] then
|
||||
groupCache[item] = get_group_item(invCache,
|
||||
item:sub(7):split(","))
|
||||
end
|
||||
|
||||
-- Move the selected item.
|
||||
if groupCache[item] then
|
||||
stack = inv:remove_item("main",
|
||||
ItemStack({name = groupCache[item], count = num}))
|
||||
inv:set_stack("craft", idx, stack)
|
||||
end
|
||||
else
|
||||
-- Move the item.
|
||||
stack = inv:remove_item("main",
|
||||
ItemStack({name = item, count = num}))
|
||||
inv:set_stack("craft", idx, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_player_inventory_action(
|
||||
function(player, action, inventory, inventory_info)
|
||||
-- Hide the autocrafting menu when the player drops an item.
|
||||
if cg.AUTOCRAFTING and inventory_info.listname == "main" then
|
||||
local context = sfinv.get_or_create_context(player)
|
||||
|
||||
if context.cg_auto_menu then
|
||||
context.cg_auto_menu = false
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
|
@ -1,123 +0,0 @@
|
|||
cg = {
|
||||
PAGE_WIDTH = 8,
|
||||
PAGE_ITEMS = 32,
|
||||
items_all = {},
|
||||
player_data = {},
|
||||
crafts = {},
|
||||
craft_types = {},
|
||||
group_stereotypes = {},
|
||||
}
|
||||
|
||||
local settings = minetest.settings
|
||||
|
||||
cg.AUTOCRAFTING = settings:get_bool("cg_plus_autocrafting", true)
|
||||
cg.GROUP_SEARCH = settings:get_bool("cg_plus_group_search", true)
|
||||
cg.GROUP_SEARCH_MAX = tonumber(settings:get("cg_plus_group_search_max")) or 5
|
||||
|
||||
cg.S = minetest.get_translator("cg_plus")
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
local path = minetest.get_modpath("cg_plus")
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
if cg.AUTOCRAFTING then
|
||||
dofile(path .. "/autocrafting.lua")
|
||||
end
|
||||
|
||||
dofile(path .. "/inventory.lua")
|
||||
|
||||
cg.register_craft_type("normal", {
|
||||
description = F(cg.S("Crafting")),
|
||||
uses_crafting_grid = true,
|
||||
alt_zero_width = "shapeless",
|
||||
|
||||
get_grid_size = function(craft)
|
||||
local width = math.max(craft.width, 1)
|
||||
local height = math.ceil(table.maxn(craft.items) / width)
|
||||
local sideLen = math.max(width, height)
|
||||
|
||||
if sideLen < 3 then
|
||||
return {x = 3, y = 3}
|
||||
else
|
||||
return {x = sideLen, y = sideLen}
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_craft_type("shapeless", {
|
||||
description = F(cg.S("Mixing")),
|
||||
inherit_width = true,
|
||||
uses_crafting_grid = true,
|
||||
|
||||
get_grid_size = function(craft)
|
||||
local numItems = table.maxn(craft.items)
|
||||
|
||||
if table.maxn(craft.items) <= 9 then
|
||||
return {x = 3, y = 3}
|
||||
else
|
||||
local sideLen = math.ceil(math.sqrt(numItems))
|
||||
return {x = sideLen, y = sideLen}
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_craft_type("cooking", {
|
||||
description = F(cg.S("Cooking")),
|
||||
inherit_width = true,
|
||||
arrow_icon = "cg_plus_arrow_bottom.png^cg_plus_icon_cooking.png",
|
||||
|
||||
get_grid_size = function(craft)
|
||||
return {x = 1, y = 1}
|
||||
end,
|
||||
|
||||
get_infotext = function(craft)
|
||||
return minetest.colorize("#FFFF00",
|
||||
F(cg.S("Time: @1 s", craft.width or 0)))
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_craft_type("fuel", {
|
||||
description = F(cg.S("Fuel")),
|
||||
inherit_width = true,
|
||||
arrow_icon = "cg_plus_arrow_bottom.png^cg_plus_icon_fuel.png",
|
||||
|
||||
get_grid_size = function(craft)
|
||||
return {x = 1, y = 1}
|
||||
end,
|
||||
|
||||
get_infotext = function(craft)
|
||||
return minetest.colorize("#FFFF00",
|
||||
F(cg.S("Time: @1 s", craft.time or 0)))
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_craft_type("digging", {
|
||||
description = F(cg.S("Digging")),
|
||||
inherit_width = true,
|
||||
arrow_icon = "cg_plus_arrow_bottom.png^cg_plus_icon_digging.png",
|
||||
|
||||
get_grid_size = function(craft)
|
||||
return {x = 1, y = 1}
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_craft_type("digging_chance", {
|
||||
description = F(cg.S("Digging@n(by chance)")),
|
||||
inherit_width = true,
|
||||
arrow_icon = "cg_plus_arrow_bottom.png^cg_plus_icon_digging.png",
|
||||
|
||||
get_grid_size = function(craft)
|
||||
return {x = 1, y = 1}
|
||||
end
|
||||
})
|
||||
|
||||
cg.register_group_stereotype("mesecon_conductor_craftable",
|
||||
"mesecons:wire_00000000_off")
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
cg.register_group_stereotype("stone", "default:stone")
|
||||
cg.register_group_stereotype("wood", "default:wood")
|
||||
cg.register_group_stereotype("sand", "default:sand")
|
||||
cg.register_group_stereotype("leaves", "default:leaves")
|
||||
cg.register_group_stereotype("tree", "default:tree")
|
||||
end
|
|
@ -1,351 +0,0 @@
|
|||
local F = minetest.formspec_escape
|
||||
|
||||
function cg.update_filter(player, context, filter, force)
|
||||
filter = filter or ""
|
||||
if not force and filter:lower() == (context.cg_filter or ""):lower() then
|
||||
return
|
||||
end
|
||||
|
||||
context.cg_page = 0
|
||||
context.cg_filter = filter
|
||||
cg.filter_items(player, context.cg_filter)
|
||||
end
|
||||
|
||||
function cg.update_selected_item(player, context, item, force)
|
||||
if not force and item == context.cg_selected_item then
|
||||
return
|
||||
end
|
||||
|
||||
if item then
|
||||
context.cg_craft_page = 0
|
||||
end
|
||||
|
||||
context.cg_selected_item = item
|
||||
context.cg_auto_menu = false
|
||||
end
|
||||
|
||||
local function make_item_button(formspec, x, y, size, name)
|
||||
if name and name ~= "" then
|
||||
local groups, buttonText
|
||||
|
||||
if name:sub(1, 6) == "group:" then
|
||||
groups = name:sub(7):split(",")
|
||||
buttonText = #groups == 1 and "G" or ("G " .. #groups)
|
||||
name = name:gsub(",", "/")
|
||||
end
|
||||
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"item_image_button[%.2f,%.2f;%.2f,%.2f;%s;cgitem_%s;%s]",
|
||||
x, y, size, size,
|
||||
groups and (cg.group_stereotypes[groups[1]] or "") or name,
|
||||
name:match("^%S+"), -- Keep only the item name, not the quantity.
|
||||
buttonText or ""
|
||||
)
|
||||
|
||||
if groups then
|
||||
local tooltipText
|
||||
if #groups == 1 then
|
||||
tooltipText = F(cg.S(
|
||||
"Any item in group: @1",
|
||||
minetest.colorize("#72FF63", groups[1])
|
||||
))
|
||||
else
|
||||
tooltipText = F(cg.S(
|
||||
"Any item in groups: @1",
|
||||
minetest.colorize("#72FF63", table.concat(groups, ", "))
|
||||
))
|
||||
end
|
||||
|
||||
formspec[#formspec + 1] = string.format("tooltip[cgitem_%s;%s]",
|
||||
name, tooltipText)
|
||||
end
|
||||
else
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"image[%.2f,%.2f;%.2f,%.2f;gui_hb_bg.png]",
|
||||
x, y, size, size
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local function make_item_grid(formspec, player, context)
|
||||
local itemList = cg.get_item_list(player)
|
||||
context.cg_page = context.cg_page or 0
|
||||
|
||||
-- Buttons
|
||||
formspec[#formspec + 1] =
|
||||
"real_coordinates[true]" ..
|
||||
"image_button[3.425,5.3;0.8,0.8;cg_plus_icon_search.png;cg_search;]" ..
|
||||
"image_button[4.325,5.3;0.8,0.8;cg_plus_icon_clear.png;cg_clear;]" ..
|
||||
"image_button[6.625,5.3;0.8,0.8;cg_plus_icon_prev.png;cg_prev;]" ..
|
||||
"image_button[9.325,5.3;0.8,0.8;cg_plus_icon_next.png;cg_next;]"
|
||||
|
||||
-- Search box
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"field[0.375,5.3;2.95,0.8;cg_filter;;%s]",
|
||||
F(context.cg_filter or "")
|
||||
)
|
||||
formspec[#formspec + 1] = "field_close_on_enter[cg_filter;false]"
|
||||
|
||||
-- Page number
|
||||
formspec[#formspec + 1] = string.format("label[7.75,5.7;%i / %i]",
|
||||
context.cg_page + 1, itemList.num_pages)
|
||||
|
||||
local startIdx = context.cg_page * cg.PAGE_ITEMS + 1
|
||||
local item
|
||||
|
||||
for itemIdx = 0, cg.PAGE_ITEMS - 1 do
|
||||
item = itemList.list[startIdx + itemIdx]
|
||||
|
||||
if item then
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"item_image_button[%.2f,%.2f;1,1;%s;cgitem_%s;]",
|
||||
(itemIdx % cg.PAGE_WIDTH) * 1.25 + 0.375,
|
||||
math.floor(itemIdx / cg.PAGE_WIDTH) * 1.25 + 0.375,
|
||||
item, item
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function make_craft_preview(formspec, player, context)
|
||||
formspec[#formspec + 1] =
|
||||
"real_coordinates[true]" ..
|
||||
"image_button[9.325,0.375;0.8,0.8;" ..
|
||||
"cg_plus_icon_clear.png;cg_craft_close;]" ..
|
||||
"image[0.375,0.375;0.8,0.8;gui_hb_bg.png]"
|
||||
|
||||
local item = context.cg_selected_item
|
||||
|
||||
-- Item image
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"item_image[0.375,0.375;0.8,0.8;%s]",
|
||||
item
|
||||
)
|
||||
-- Item name
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"label[1.5,0.6;%s]",
|
||||
cg.crafts[item] and minetest.registered_items[item].description or item
|
||||
)
|
||||
|
||||
-- No recipes label, if applicable
|
||||
local crafts = cg.crafts[item]
|
||||
if not crafts or #crafts == 0 then
|
||||
formspec[#formspec + 1] = string.format("label[2.875,3.2;%s]",
|
||||
F(cg.S("There are no recipes for this item.")))
|
||||
return
|
||||
end
|
||||
|
||||
-- Previous/next craft buttons, page number
|
||||
if #crafts > 1 then
|
||||
formspec[#formspec + 1] =
|
||||
"image_button[2.875,5.3;0.8,0.8;" ..
|
||||
"cg_plus_icon_prev.png;cg_craft_prev;]" ..
|
||||
"image_button[5.575,5.3;0.8,0.8;" ..
|
||||
"cg_plus_icon_next.png;cg_craft_next;]"
|
||||
formspec[#formspec + 1] = string.format("label[4,5.7;%i / %i]",
|
||||
context.cg_craft_page + 1, #crafts)
|
||||
end
|
||||
|
||||
local craft = cg.parse_craft(crafts[context.cg_craft_page + 1])
|
||||
local template = cg.craft_types[craft.type] or {}
|
||||
|
||||
-- Auto-crafting buttons
|
||||
if cg.AUTOCRAFTING and template.uses_crafting_grid then
|
||||
formspec[#formspec + 1] = "image_button[0.375,5.3;0.8,0.8;" ..
|
||||
"cg_plus_icon_autocrafting.png;cg_auto_menu;]"
|
||||
formspec[#formspec + 1] = string.format("tooltip[cg_auto_menu;%s]",
|
||||
F(cg.S("Craft this recipe")))
|
||||
|
||||
if context.cg_auto_menu then
|
||||
local num = 1
|
||||
local yPos = 4.3
|
||||
|
||||
while true do
|
||||
num = math.min(num, context.cg_auto_max)
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"button[0.375,%.2f;0.8,0.8;cg_auto_%i;%i]",
|
||||
yPos, num, num
|
||||
)
|
||||
formspec[#formspec + 1] = string.format(
|
||||
"tooltip[cg_auto_%i;%s]",
|
||||
num,
|
||||
num == 1
|
||||
and F(cg.S("Craft @1 item", num))
|
||||
or F(cg.S("Craft @1 items", num))
|
||||
)
|
||||
|
||||
if num < context.cg_auto_max then
|
||||
num = num * 10
|
||||
yPos = yPos - 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Craft type/info text
|
||||
formspec[#formspec + 1] = string.format("label[6.7,1.8;%s]",
|
||||
template.description or "")
|
||||
formspec[#formspec + 1] = string.format("label[6.7,2.4;%s]",
|
||||
craft.infotext or "")
|
||||
|
||||
-- Draw craft item grid, feat. maths.
|
||||
|
||||
-- Determine max number of grid slots that could fit on one side.
|
||||
-- Squares shouldn't take up more than a third of the grid area.
|
||||
local gridMax = math.max(math.max(craft.grid_size.x, craft.grid_size.y), 3)
|
||||
-- Determine distance between crafting grid slots.
|
||||
-- <grid area side length> / (<num slots per side> - <extra padding>)
|
||||
local slotDist = 3.5 / (gridMax - 0.2)
|
||||
|
||||
-- Determine upper-left corner of crafting squares
|
||||
-- <right x of grid area> - (<grid width> - <extra padding>) * slotDist
|
||||
local xOffset = 6.375 - (craft.grid_size.x - 0.2) * slotDist
|
||||
-- <center y of grid area> -
|
||||
-- (<grid height> - <extra padding>) * slotDist * 0.5
|
||||
local yOffset = 3.2 - (craft.grid_size.y - 0.2) * slotDist * 0.5
|
||||
|
||||
for idx = 1, craft.grid_size.x * craft.grid_size.y do
|
||||
make_item_button(
|
||||
formspec,
|
||||
((idx - 1) % craft.grid_size.x) * slotDist + xOffset,
|
||||
math.floor((idx - 1) / craft.grid_size.y) * slotDist + yOffset,
|
||||
slotDist * 0.8, -- 1 - <padding amount>
|
||||
craft.items[idx]
|
||||
)
|
||||
end
|
||||
|
||||
-- Craft arrow
|
||||
formspec[#formspec + 1] = string.format("image[6.625,2.7;1,1;%s]",
|
||||
template.arrow_icon or "cg_plus_arrow.png")
|
||||
|
||||
-- Craft output
|
||||
make_item_button(formspec, 7.875, 2.7, 1, craft.output)
|
||||
end
|
||||
|
||||
--[[
|
||||
sfinv registration
|
||||
]]
|
||||
|
||||
local function page_get(self, player, context)
|
||||
local formspec = {}
|
||||
|
||||
if context.cg_selected_item then
|
||||
make_craft_preview(formspec, player, context)
|
||||
else
|
||||
make_item_grid(formspec, player, context)
|
||||
end
|
||||
|
||||
return sfinv.make_formspec(player, context, table.concat(formspec), true)
|
||||
end
|
||||
|
||||
local function page_on_player_receive_fields(self, player, context, fields)
|
||||
if fields.cg_craft_close then
|
||||
context.cg_selected_item = nil
|
||||
context.cg_auto_menu = false
|
||||
elseif fields.cg_prev and context.cg_page then
|
||||
context.cg_page = context.cg_page - 1
|
||||
elseif fields.cg_next and context.cg_page then
|
||||
context.cg_page = context.cg_page + 1
|
||||
elseif fields.cg_craft_prev and context.cg_craft_page then
|
||||
context.cg_craft_page = context.cg_craft_page - 1
|
||||
context.cg_auto_menu = false
|
||||
elseif fields.cg_craft_next and context.cg_craft_page then
|
||||
context.cg_craft_page = context.cg_craft_page + 1
|
||||
context.cg_auto_menu = false
|
||||
elseif fields.cg_search or fields.key_enter_field == "cg_filter" then
|
||||
cg.update_filter(player, context, fields.cg_filter)
|
||||
elseif fields.cg_clear then
|
||||
cg.update_filter(player, context, "", true)
|
||||
elseif fields.cg_auto_menu and cg.AUTOCRAFTING then
|
||||
if not context.cg_auto_menu then
|
||||
-- Make sure the craft is valid, in case the client is sending
|
||||
-- fake formspec fields.
|
||||
local crafts = cg.crafts[context.cg_selected_item] or {}
|
||||
local craft = crafts[context.cg_craft_page + 1]
|
||||
|
||||
if craft and cg.craft_types[craft.type]
|
||||
and cg.craft_types[craft.type].uses_crafting_grid then
|
||||
context.cg_auto_menu = true
|
||||
context.cg_auto_max = cg.auto_get_craftable(player, craft)
|
||||
end
|
||||
else
|
||||
context.cg_auto_menu = false
|
||||
end
|
||||
else
|
||||
for field, _ in pairs(fields) do
|
||||
if field:sub(1, 7) == "cgitem_" then
|
||||
local item = string.sub(field, 8)
|
||||
|
||||
if item:sub(1, 6) == "group:" then
|
||||
if cg.GROUP_SEARCH then
|
||||
cg.update_filter(player, context, item)
|
||||
cg.update_selected_item(player, context, nil)
|
||||
elseif cg.group_stereotypes[item:sub(7)] then
|
||||
cg.update_selected_item(player, context,
|
||||
cg.group_stereotypes[item:sub(7)])
|
||||
end
|
||||
else
|
||||
cg.update_selected_item(player, context, item)
|
||||
end
|
||||
|
||||
break
|
||||
elseif field:sub(1, 8) == "cg_auto_"
|
||||
and context.cg_auto_menu then
|
||||
-- No need to sanity check, we already did that when
|
||||
-- showing the autocrafting menu.
|
||||
local num = tonumber(field:sub(9))
|
||||
|
||||
if num > 0 and num <= context.cg_auto_max then
|
||||
cg.auto_craft(
|
||||
player,
|
||||
cg.crafts[context.cg_selected_item]
|
||||
[context.cg_craft_page + 1],
|
||||
num
|
||||
)
|
||||
sfinv.set_page(player, "sfinv:crafting")
|
||||
end
|
||||
|
||||
context.cg_auto_menu = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Wrap around when the player presses the next button on the last
|
||||
-- page, or the previous button on the first.
|
||||
if context.cg_page then
|
||||
context.cg_page = context.cg_page %
|
||||
math.max(cg.get_item_list(player).num_pages, 1)
|
||||
end
|
||||
|
||||
if context.cg_craft_page then
|
||||
context.cg_craft_page = context.cg_craft_page %
|
||||
math.max(#(cg.crafts[context.cg_selected_item] or {}), 1)
|
||||
end
|
||||
|
||||
-- Update the formspec.
|
||||
sfinv.set_player_inventory_formspec(player, context)
|
||||
end
|
||||
|
||||
local function page_on_leave(self, player, context)
|
||||
context.cg_auto_menu = false
|
||||
end
|
||||
|
||||
if sfinv.pages["mtg_craftguide:craftguide"] ~= nil then
|
||||
-- Override MTG's crafting guide
|
||||
sfinv.override_page("mtg_craftguide:craftguide", {
|
||||
title = F(cg.S("Crafting Guide")),
|
||||
get = page_get,
|
||||
on_player_receive_fields = page_on_player_receive_fields,
|
||||
on_leave = page_on_leave
|
||||
})
|
||||
else
|
||||
sfinv.register_page("cg_plus:crafting_guide", {
|
||||
title = F(cg.S("Crafting Guide")),
|
||||
get = page_get,
|
||||
on_player_receive_fields = page_on_player_receive_fields,
|
||||
on_leave = page_on_leave
|
||||
})
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
# textdomain:cg_plus
|
||||
|
||||
Crafting=
|
||||
Mixing=
|
||||
Cooking=
|
||||
Fuel=
|
||||
Time: @1 s=
|
||||
Digging=
|
||||
Digging@n(by chance)=
|
||||
Crafting Guide=
|
||||
Item could not be crafted!=
|
||||
Any item in group: @1=
|
||||
Any item in groups: @1=
|
||||
Craft @1 item=
|
||||
Craft @1 items=
|
||||
There are no recipes for this item.=
|
|
@ -1,4 +0,0 @@
|
|||
name = cg_plus
|
||||
description = An intuitive and full-featured craft guide and autocrafting mod which is compatible with sfinv.
|
||||
depends = sfinv
|
||||
optional_depends = mtg_craftguide
|
Before Width: | Height: | Size: 795 KiB |
|
@ -1,8 +0,0 @@
|
|||
# Enable the option to automatically move items to the craft grid from a craft preview window.
|
||||
cg_plus_autocrafting (Autocrafting) bool true
|
||||
|
||||
# Search for items by group when a search begins with "group:".
|
||||
cg_plus_group_search (Search by item groups) bool true
|
||||
|
||||
# Limit the number of groups which can be searched for at once when group search is enabled.
|
||||
cg_plus_group_search_max (Group search max) int 5 1 100
|
Before Width: | Height: | Size: 178 B |
Before Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 758 B |
Before Width: | Height: | Size: 210 B |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 131 B |
Before Width: | Height: | Size: 132 B |
Before Width: | Height: | Size: 162 B |
|
@ -2,4 +2,4 @@ description = GUI related mods (Inventory, ...)
|
|||
|
||||
minetest_show_wielded_item - show name of block in hotbar
|
||||
minetest_hudbars - adds custom HUD- bars (life, hunger, ...)
|
||||
cg_plus - Crafting Guide Plus
|
||||
craftguide - Crafting Guide
|
||||
|
|