Skip to content

Commit

Permalink
Bunch of updates
Browse files Browse the repository at this point in the history
- Update and document public API
- Fixes to group handling
- New clear icon
  • Loading branch information
random-geek committed Apr 21, 2024
1 parent ab5d530 commit 6511eb8
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 98 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,22 @@ Note: If `mtg_craftguide` is present, CGP will override its page in the inventor

## 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.
- "Intelligent" auto-crafting, or rather, automatic craft staging.
- 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.
- Group support, including group search (try searching `group:wood` in the craft guide) and support for craft recipes
requiring items in one or more groups.
- Various settings for server owners

## For Mod/Game Developers

For information on the Crafting Guide Plus API and other tips for modders, see [developer_docs.md](developer_docs.md).

## 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.
greatest count from the player's inventory will be utilized.
- Items in multiple groups will not always display correctly in craft view.

## License
Expand Down
138 changes: 89 additions & 49 deletions api.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
-- TODO: aliases?

local custom_crafts = {}

local function get_drops(item, def)
local normalDrops = {}
local randomDrops = {}
Expand Down Expand Up @@ -55,18 +57,65 @@ local function get_drops(item, def)
return normalDrops, randomDrops
end

local function build_group_stereotypes_list()
-- Remember: Some group stereotypes are already registered
local startTime = minetest.get_us_time()
local usedMultiGroups = {}

for _, recipes in pairs(cg.crafts) do
for _, recipe in ipairs(recipes) do
for _, item in ipairs(recipe.items) do
if item:sub(1, 6) == "group:" then
local groupsString = item:sub(7)
local groupsTable = groupsString:split(",")
if #groupsTable > 1 then
usedMultiGroups[groupsString] = groupsTable
end
end
end
end
end

for _, item in ipairs(cg.items_all.list) do
local groups = minetest.registered_items[item].groups

for group, _ in pairs(groups) do
if cg.group_stereotypes[group] == nil then
cg.group_stereotypes[group] = item
end
end

for clusterString, clusterTable in pairs(usedMultiGroups) do
if cg.group_stereotypes[clusterString] == nil then
local match = true
for _, group in ipairs(clusterTable) do
if not groups[group] then
match = false
break
end
end
if match then
cg.group_stereotypes[clusterString] = item
end
end
end
end

minetest.log("info", string.format("[cg_plus] Finished building group stereotype list in %.3f s.",
(minetest.get_us_time() - startTime) / 1000000))
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
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 {}
table.insert_all(cg.crafts[item], custom_crafts[item] or {})
end
end

Expand All @@ -83,7 +132,7 @@ function cg.build_item_list()

if fuel.time > 0 then
table.insert(cg.crafts[item], {
type = "fuel",
method = "fuel",
items = {item},
output = decremented.items[1]:to_string(),
time = fuel.time,
Expand All @@ -96,7 +145,7 @@ function cg.build_item_list()
for dItem, dCount in pairs(normalDrops) do
if cg.crafts[dItem] then
table.insert(cg.crafts[dItem], {
type = "digging",
method = "digging",
width = 0,
items = {item},
output = ItemStack({
Expand All @@ -110,7 +159,7 @@ function cg.build_item_list()
for dItem, dCount in pairs(randomDrops) do
if cg.crafts[dItem] then
table.insert(cg.crafts[dItem], {
type = "digging_chance",
method = "digging_chance",
width = 0,
items = {item},
output = ItemStack({
Expand All @@ -121,12 +170,6 @@ function cg.build_item_list()
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)
Expand All @@ -138,6 +181,8 @@ function cg.build_item_list()
(minetest.get_us_time() - startTime) / 1000000
)
)

build_group_stereotypes_list()
end

function cg.filter_items(player, filter)
Expand Down Expand Up @@ -192,56 +237,51 @@ function cg.filter_items(player, filter)
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 ""
local method
if craft.method == "normal" and craft.width == 0 then -- Special rules for shapeless recipes
method = "shapeless"
else
method = craft.method
end

local width = math.max(craft.width or 0, 1)
local template = cg.craft_methods[method] or {}

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
local gridSize = (template.get_grid_size and template.get_grid_size(craft)) or {x = 3, y = 3}
local width = craft.width or 0
local items = {}

if template.inherit_width then
-- For shapeless recipes, there is no need to modify the item list.
newCraft.items = craft.items
if width == 0 then
-- Shapeless recipes
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
-- The craft's width is not always the same as the grid size, so items need to be shifted around.
for i, item in pairs(craft.items) do
items[i + (gridSize.x - width) * math.floor((i - 1) / width)] = item
end
end

return newCraft
return {
method = method,
infotext = (template.get_infotext and template.get_infotext(craft)) or "",
grid_size = gridSize,
width = width,
items = items,
output = craft.output or "",
}
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
function cg.register_crafting_method(name, def)
cg.craft_methods[name] = def
end

function cg.register_craft(recipe, assign_to)
local item = ItemStack(assign_to or recipe.output):get_name() -- Removes quantity, etc. from itemstring
custom_crafts[item] = custom_crafts[item] or {}
table.insert(custom_crafts[item], recipe)
end

function cg.register_group_stereotype(group, item)
Expand Down
93 changes: 93 additions & 0 deletions developer_docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Crafting Guide Plus developer documentation

## Groups

cg_plus respects the standard groups `not_in_creative_inventory` and `not_in_craft_guide`.
Adding either of these to an item/node definition will make it not appear in the crafting guide.

## API

### `cg.register_crafting_method(name, def)`

Adds or overrides a type of craft in cg_plus, for use with `cg.register_craft`. Default craft methods are `normal`,
`shapeless`, `cooking`, `fuel`, `digging`, and `digging_chance`.

#### Parameters

* `name` (string): The name of the new craft method. Matches the `method` field in craft definitions.
* `def` (table): A craft method definition with the following fields:
* `description` (string): A human-readable name for the crafting method, which will be shown next to recipes in the
crafting guide.
* `arrow_icon` (string): Texture to use for the arrow icon in recipes. Default is a plain arrow.
* `uses_crafting_grid` (bool): Setting to `true` allows crafts of this method to be automatically staged in the
default crafting grid when autocrafting is enabled.
* `get_grid_size = function(craft)`: Used to calculate the shape of the crafting grid displayed for each recipe.
Takes a recipe defintion `craft` and returns a table in the format `{x = width, y = height}`.
* `get_infotext = function(craft)`: Optional, used to add additional information to a recipe page, e.g. cooking or
burning times. Takes a recipe defintion `craft` and returns a string.

#### Example

See below.

### `cg.register_craft(recipe, [assign_to])`

Registers a craft to appear only in the crafting guide, independent of `minetest.register_craft`. Useful for mods that
implement crafting outside the default crafting grid.

#### Parameters

* `recipe` (table): Possible keys:
* `method` (string): Can be an official crafting method or one created with `cg.register_craft`.
* `width` (integer): Width of the recipe inputs, which may be less than the width of the crafting grid. If zero, the
recipe will expand to the full width of the crafting grid.
* `items` (table): One-dimensional table of input item names, listed from left-to-right and top-to-bottom. May be
groups such as `group:dye` or `group:dye,color_violet`.
* `output` (string): Output itemstring, e.g. `default:stone` or `default:wood 4`.
* Additional fields can be added (e.g. cooking time) which can be displayed using `get_infotext` in
`cg.register_crafting_method`. The `items` and `width` fields are reserved.
* `assign_to` (string): Optional itemstring; if specified, the craft will be assigned to this item rather than the
output item. Useful for fuel recipes that consume the input, etc.

#### Example

Register a craft for a theoretical mod `woodmod` which allows sawing of stairs using a table saw:

```lua
cg.register_crafting_method("woodmod_table_saw", {
description = "Table Saw",
arrow_icon = "cg_plus_arrow_bottom.png^woodmod_icon_saw.png",
uses_crafting_grid = false,
get_grid_size = function(craft)
return {x = 4, y = 4}
end,
get_infotext = function(craft)
return string.format("Cutting time: %i seconds", craft.cutting_time)
end,
})

cg.register_craft({
method = "woodmod_table_saw",
width = 2,
items = {"group:wood", "", "group:wood", "group:wood"},
output = "stairs:stair_wood 4",
cutting_time = 10,
})
```

### `cg.register_group_stereotype(group, item)`

Adds or overrides a group stereotype. When a recipe takes a generic item in the given group, the given item will be
displayed instead of a randomly-chosen item in that group. Clicking on the item button with group search disabled will
also search for the stereotype item.

`group` can be multiple comma-separated groups (e.g. `dye,color_blue`) for use by recipes with multi-group items. The
order of the groups *does* matter.

#### Example

Show yellow dye as the default for items in the `dye` group:

```lua
cg.register_group_stereotype("dye", "dye:yellow")
```
Loading

0 comments on commit 6511eb8

Please sign in to comment.