Compare commits

..

No commits in common. "main" and "1.7.3" have entirely different histories.
main ... 1.7.3

64 changed files with 750 additions and 1454 deletions

View File

@ -10,7 +10,6 @@ read_globals = {
"armor", "armor",
"skins", "skins",
"awards", "awards",
"hb",
"vector", "vector",
"string", "string",
"table", "table",
@ -31,7 +30,6 @@ exclude_files = {
"tests/test_custom_recipes.lua", "tests/test_custom_recipes.lua",
"tests/test_operators.lua", "tests/test_operators.lua",
"tests/test_tabs.lua", "tests/test_tabs.lua",
"tests/test_waypoints.lua",
".install", ".install",
".luarocks", ".luarocks",

168
API.md
View File

@ -1,19 +1,6 @@
# API :screwdriver: ## API
### Table of Contents ### Custom tabs
1. [**Tabs**](https://github.com/minetest-mods/i3/blob/main/API.md#tabs)
2. [**Recipes**](https://github.com/minetest-mods/i3/blob/main/API.md#recipes)
3. [**Minitabs**](https://github.com/minetest-mods/i3/blob/main/API.md#minitabs)
4. [**Recipe filters**](https://github.com/minetest-mods/i3/blob/main/API.md#recipe-filters)
5. [**Search filters**](https://github.com/minetest-mods/i3/blob/main/API.md#search-filters)
6. [**Sorting methods**](https://github.com/minetest-mods/i3/blob/main/API.md#sorting-methods)
7. [**Item list compression**](https://github.com/minetest-mods/i3/blob/main/API.md#item-list-compression)
8. [**Waypoints**](https://github.com/minetest-mods/i3/blob/main/API.md#waypoints)
9. [**Miscellaneous**](https://github.com/minetest-mods/i3/blob/main/API.md#miscellaneous)
---
### Tabs
#### `i3.new_tab(name, def)` #### `i3.new_tab(name, def)`
@ -25,62 +12,49 @@ Custom tabs can be added to the `i3` inventory as follow (example):
```Lua ```Lua
i3.new_tab("stuff", { i3.new_tab("stuff", {
description = "Stuff", description = "Stuff",
image = "image.png", -- Optional, add an image next to the tab description image = "image.png", -- Optional, adds an image next to the tab description
slots = true -- Optional, whether the inventory slots are shown or not. Disabled by default.
-- -- Determine if the tab is visible by a player, `false` or `nil` hide the tab
-- The functions below are all optional
--
-- Determine if the tab is visible by a player, return false to hide the tab
access = function(player, data) access = function(player, data)
local name = player:get_player_name() local name = player:get_player_name()
return name == "singleplayer" return name == "singleplayer"
end, end,
-- Build the formspec
formspec = function(player, data, fs) formspec = function(player, data, fs)
fs("label", 3, 1, "Just a test") fs("label[3,1;This is just a test]")
fs"label[3,2;Lorem Ipsum]"
-- No need to return anything
end, end,
-- Events handling happens here -- Events handling happens here
fields = function(player, data, fields) fields = function(player, data, fields)
if fields.mybutton then
-- Do things
end
-- To prevent a formspec update, return false.
-- Otherwise: no need to return anything, it's automatic.
end, end,
}) })
``` ```
- `player` is an `ObjectRef` to the user. - `player` is an `ObjectRef` to the user.
- `data` are the user data. - `data` are the user data.
- `fs` is the formspec table which is callable with a metamethod. Every call adds a new entry. - `fs` is the formspec table which is callable with a metamethod. Each call adds a new entry.
#### `i3.set_fs(player)` #### `i3.set_fs(player)`
Update the current formspec. Updates the current formspec.
#### `i3.remove_tab(tabname)` #### `i3.remove_tab(tabname)`
Delete a tab by name. Deletes a tab by name.
#### `i3.get_current_tab(player)` #### `i3.get_current_tab(player)`
Return the current player tab. `player` is an `ObjectRef` to the user. Returns the current player tab. `player` is an `ObjectRef` to the user.
#### `i3.set_tab(player[, tabname])` #### `i3.set_tab(player[, tabname])`
Set the current tab by name. `player` is an `ObjectRef` to the user. Sets the current tab by name. `player` is an `ObjectRef` to the user.
`tabname` can be omitted to get an empty tab. `tabname` can be omitted to get an empty tab.
#### `i3.override_tab(tabname, def)` #### `i3.override_tab(tabname, def)`
Override a tab by name. `def` is the tab definition like seen in `i3.set_tab` Overrides a tab by name. `def` is the tab definition like seen in `i3.set_tab`.
#### `i3.tabs` #### `i3.tabs`
@ -88,17 +62,15 @@ A list of registered tabs.
--- ---
### Recipes ### Custom recipes
Custom recipes are nonconventional crafts outside the main crafting grid. Custom recipes are nonconventional crafts outside the main crafting grid.
They can be registered in-game dynamically and have a size beyond 3x3 items. They can be registered in-game dynamically and have a size beyond 3x3 items.
**Note:** the registration format differs from the default registration format in everything. **Note:** the registration format differs from the default registration format in everything.
The width is automatically calculated depending where you place the commas. The width is automatically calculated depending where you place the commas. Look at the examples attentively.
Examples: #### Registering a custom crafting type (example)
#### Registering a custom crafting type
```Lua ```Lua
i3.register_craft_type("digging", { i3.register_craft_type("digging", {
@ -107,7 +79,7 @@ i3.register_craft_type("digging", {
}) })
``` ```
#### Registering a custom crafting recipe #### Registering a custom crafting recipe (examples)
```Lua ```Lua
i3.register_craft { i3.register_craft {
@ -146,7 +118,7 @@ i3.register_craft {
} }
``` ```
Multiple recipes can also be registered at once: Multiples recipes can also be registered:
```Lua ```Lua
i3.register_craft { i3.register_craft {
@ -180,53 +152,6 @@ i3.register_craft {
--- ---
### Minitabs
Manage the tabs on the right panel of the inventory.
Allow to make a sensible list sorted by specific groups of items.
#### `i3.new_minitab(name, def)`
Add a new minitab (limited to 6).
- `name` is the tab name.
- `def` is the definition table.
Example:
```Lua
i3.new_minitab("test", {
description = "Test",
-- Whether this tab is visible or not. Optional.
access = function(player, data)
return player:get_player_name() == "singleplayer"
end,
-- Whether a specific item is shown in the list or not.
sorter = function(item, data)
return item:find"wood"
end
})
```
- `player` is an `ObjectRef` to the user.
- `data` are the user data.
- `item` is an item name string.
#### `i3.remove_minitab(name)`
Remove a minitab by name.
- `name` is the name of the tab to remove.
#### `i3.minitabs`
A list of registered minitabs.
---
### Recipe filters ### Recipe filters
Recipe filters can be used to filter the recipes shown to players. Progressive Recipe filters can be used to filter the recipes shown to players. Progressive
@ -234,7 +159,7 @@ mode is implemented as a recipe filter.
#### `i3.add_recipe_filter(name, function(recipes, player))` #### `i3.add_recipe_filter(name, function(recipes, player))`
Add a recipe filter with the given `name`. The filter function returns the Adds a recipe filter with the given `name`. The filter function returns the
recipes to be displayed, given the available recipes and an `ObjectRef` to the recipes to be displayed, given the available recipes and an `ObjectRef` to the
user. Each recipe is a table of the form returned by user. Each recipe is a table of the form returned by
`minetest.get_craft_recipe`. `minetest.get_craft_recipe`.
@ -256,7 +181,7 @@ end)
#### `i3.set_recipe_filter(name, function(recipe, player))` #### `i3.set_recipe_filter(name, function(recipe, player))`
Remove all recipe filters and add a new one. Removes all recipe filters and adds a new one.
#### `i3.recipe_filters` #### `i3.recipe_filters`
@ -282,7 +207,7 @@ Notes:
#### `i3.add_search_filter(name, function(item, values))` #### `i3.add_search_filter(name, function(item, values))`
Add a search filter. Adds a search filter.
The search function must return a boolean value (whether the given item should be listed or not). The search function must return a boolean value (whether the given item should be listed or not).
- `name` is the filter name. - `name` is the filter name.
@ -316,7 +241,7 @@ Sorting methods are used to filter the player's main inventory.
#### `i3.add_sorting_method(name, def)` #### `i3.add_sorting_method(name, def)`
Add a player inventory sorting method. Adds a player inventory sorting method.
- `name` is the method name. - `name` is the method name.
- `def` is the method definition. - `def` is the method definition.
@ -351,7 +276,7 @@ A table containing all sorting methods.
#### `i3.compress(item, def)` #### `i3.compress(item, def)`
Add a new group of items to compress. Adds a new group of items to compress.
- `item` is the item which represent the group of compressed items. - `item` is the item which represent the group of compressed items.
- `def` is a table specifying the substring replace patterns to be used. - `def` is a table specifying the substring replace patterns to be used.
@ -372,54 +297,11 @@ A map of all compressed item groups, indexed by stereotypes.
--- ---
### Waypoints
`i3` allows you to manage the waypoints of a specific player.
#### `i3.add_waypoint(player_name, def)`
Add a waypoint to specific player.
- `player_name` is the player name.
- `def` is the waypoint definition table.
Example:
```Lua
i3.add_waypoint("Test", {
player = "singleplayer",
pos = {x = 0, y = 2, z = 0},
color = 0xffff00,
-- image = "heart.png" (optional)
})
```
#### `i3.remove_waypoint(player_name, waypoint_name)`
Remove a waypoint for specific player.
- `player_name` is the player name.
- `waypoint_name` is the waypoint name.
Example:
```Lua
i3.remove_waypoint("singleplayer", "Test")
```
#### `i3.get_waypoints(player_name)`
Return a table of all waypoints of a specific player.
- `player_name` is the player name.
---
### Miscellaneous ### Miscellaneous
#### `i3.hud_notif(name, msg[, img])` #### `i3.hud_notif(name, msg[, img])`
Show a Steam-like HUD notification on the bottom-left corner of the screen. Shows a Steam-like HUD notification on the bottom-right corner of the screen (experimental).
- `name` is the player name. - `name` is the player name.
- `msg` is the HUD message to show. - `msg` is the HUD message to show.
@ -427,7 +309,7 @@ Show a Steam-like HUD notification on the bottom-left corner of the screen.
#### `i3.get_recipes(item)` #### `i3.get_recipes(item)`
Return a table of recipes and usages of `item`. Returns a table of recipes and usages of `item`.
#### `i3.export_url` #### `i3.export_url`
@ -441,4 +323,4 @@ given a number between 1 and 4.
--- ---
**[1]** Add `i3` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`. **¹** Add `i3` to the `secure.http_mods` or `secure.trusted_mods` setting in `minetest.conf`.

12
LICENSE
View File

@ -3,7 +3,7 @@ License of source code
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2020-2023 Jean-Patrick Guerrero and contributors. Copyright (c) 2020-2021 Jean-Patrick Guerrero and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -23,16 +23,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
Licenses of media (sounds)
--------------------------
Lone_Wolf (CC0):
i3_tab.ogg
i3_click.ogg
i3_cannot.ogg
MadPanCake (CC0):
i3_craft.ogg (https://freesound.org/people/MadPanCake/sounds/567849/)
Licenses of media (textures) Licenses of media (textures)
---------------------------- ----------------------------

View File

@ -1,13 +1,13 @@
![logo](https://user-images.githubusercontent.com/7883281/145490041-d91d6bd6-a654-438d-b208-4d5736845ab7.png) ![logo](https://user-images.githubusercontent.com/7883281/145490041-d91d6bd6-a654-438d-b208-4d5736845ab7.png)
[![GitHub Release](https://img.shields.io/github/release/minetest-mods/i3.svg?style=flat)]() ![workflow](https://github.com/minetest-mods/i3/actions/workflows/luacheck.yml/badge.svg) [![ContentDB](https://content.minetest.net/packages/jp/i3/shields/downloads/)](https://content.minetest.net/packages/jp/i3/) [![PayPal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/jpg84240) [![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](https://github.com/tterb/atomic-design-ui/blob/master/LICENSEs) [![GitHub Release](https://img.shields.io/github/release/minetest-mods/i3.svg?style=flat)]() ![workflow](https://github.com/minetest-mods/i3/actions/workflows/luacheck.yml/badge.svg) [![ContentDB](https://content.minetest.net/packages/jp/i3/shields/downloads/)](https://content.minetest.net/packages/jp/i3/) [![PayPal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/jpg84240)
#### **`i3`** is a next-generation inventory for Minetest. #### **`i3`** is a next-generation inventory for Minetest.
This mod features a modern, powerful inventory menu with a good user experience. This mod features a modern, powerful inventory menu with a good user experience.
**`i3`** provides a rich [**API**](https://github.com/minetest-mods/i3/blob/master/API.md) for mod developers who want to extend it. **`i3`** provides a rich [**API**](https://github.com/minetest-mods/i3/blob/master/API.md) for mod developers who want to extend it.
This mod requires **Minetest 5.6+** This mod requires **Minetest 5.4+**
#### List of features: #### List of features:
- Crafting Guide (survival mode only) - Crafting Guide (survival mode only)
@ -35,6 +35,7 @@ To enable it: `i3_progressive_mode = true` in `minetest.conf`.*
To use this mod in the best conditions: To use this mod in the best conditions:
- Use LuaJIT - Use LuaJIT
- Use a HiDPI widescreen display
- Use the default Freetype font style - Use the default Freetype font style
#### Troubleshooting #### Troubleshooting
@ -42,8 +43,6 @@ To use this mod in the best conditions:
If the inventory's font size is too big on certain setups (namely Windows 10/11 or 144 DPI display), you should lower the If the inventory's font size is too big on certain setups (namely Windows 10/11 or 144 DPI display), you should lower the
value of the setting `display_density_factor` in your `minetest.conf`. Note that the change is applied after restart. value of the setting `display_density_factor` in your `minetest.conf`. Note that the change is applied after restart.
You can also use the font size slider in the inventory, settings window.
#### Notes #### Notes
`i3` uses a larger inventory than the usual inventories in Minetest games. `i3` uses a larger inventory than the usual inventories in Minetest games.
@ -54,4 +53,4 @@ Report bugs on the [**Bug Tracker**](https://github.com/minetest-mods/i3/issues)
**Video review on YouTube:** https://www.youtube.com/watch?v=Xd14BCdEZ3o **Video review on YouTube:** https://www.youtube.com/watch?v=Xd14BCdEZ3o
![Preview](https://user-images.githubusercontent.com/7883281/185755315-23c2fffa-203d-4115-9dc3-576c92615733.png) ![Preview](https://content.minetest.net/uploads/3abf3755de.png)

View File

@ -20,24 +20,25 @@ local function lf(path)
end end
i3 = { i3 = {
version = 1161, version = 173,
data = core.deserialize(storage:get_string"data") or {}, data = core.deserialize(storage:get_string"data") or {},
settings = { settings = {
debug_mode = false, debug_mode = false,
max_favs = 6, max_favs = 6,
max_waypoints = 30, max_waypoints = 30,
min_fs_version = 6, min_fs_version = 4,
item_btn_size = 1.1, item_btn_size = 1.1,
drop_bag_on_die = true, drop_bag_on_die = true,
wielditem_fade_after = 3,
save_interval = 600, -- Player data save interval (in seconds) save_interval = 600, -- Player data save interval (in seconds)
hud_speed = 3, hud_speed = 1,
hud_timer_max = 3, hud_timer_max = 1.5,
damage_enabled = core.settings:get_bool"enable_damage", damage_enabled = core.settings:get_bool"enable_damage",
progressive_mode = core.settings:get_bool"i3_progressive_mode", progressive_mode = core.settings:get_bool"i3_progressive_mode",
legacy_inventory = core.settings:get_bool"i3_legacy_inventory",
item_compression = core.settings:get_bool("i3_item_compression", true),
}, },
categories = { categories = {
@ -51,26 +52,10 @@ i3 = {
saves = { -- Metadata to save saves = { -- Metadata to save
bag = true, bag = true,
home = true, home = true,
sort = true,
collapse = true,
font_size = true,
hide_tabs = true,
waypoints = true, waypoints = true,
inv_items = true, inv_items = true,
auto_sorting = true, drop_items = true,
inv_compress = true,
known_recipes = true, known_recipes = true,
wielditem_hud = true,
ignore_hotbar = true,
reverse_sorting = true,
legacy_inventory = true,
},
default_data = {
sort = 1,
font_size = 0,
collapse = true,
inv_compress = true,
}, },
files = { files = {
@ -101,7 +86,6 @@ i3 = {
groups = {}, groups = {},
plants = {}, plants = {},
modules = {}, modules = {},
minitabs = {},
craft_types = {}, craft_types = {},
recipe_filters = {}, recipe_filters = {},
@ -109,11 +93,12 @@ i3 = {
sorting_methods = {}, sorting_methods = {},
} }
i3.settings.hotbar_len = i3.settings.legacy_inventory and 8 or 9
i3.settings.inv_size = 4 * i3.settings.hotbar_len
i3.files.common() i3.files.common()
i3.files.api(http) i3.files.api(http)
i3.files.compress() i3.files.compress()
i3.files.detached()
i3.files.fields()
i3.files.groups() i3.files.groups()
i3.files.callbacks(http, storage) i3.files.callbacks(http, storage)
@ -123,8 +108,7 @@ end
if i3.settings.debug_mode then if i3.settings.debug_mode then
lf("/tests/test_tabs.lua")() lf("/tests/test_tabs.lua")()
lf("/tests/test_waypoints.lua")() lf("/tests/test_operators.lua")()
-- lf("/tests/test_operators.lua")()
lf("/tests/test_compression.lua")() lf("/tests/test_compression.lua")()
lf("/tests/test_custom_recipes.lua")() lf("/tests/test_custom_recipes.lua")()
end end

View File

@ -1,4 +1,4 @@
name = i3 name = i3
description = Next-generation inventory description = Next-generation inventory
optional_depends = 3d_armor, skinsdb, awards optional_depends = 3d_armor, skinsdb, awards
min_minetest_version = 5.6 min_minetest_version = 5.4

View File

@ -1,2 +1,8 @@
# The progressive mode shows recipes you can craft from items you ever had in your inventory. # The progressive mode shows recipes you can craft from items you ever had in your inventory.
i3_progressive_mode (Learn crafting recipes progressively) bool false i3_progressive_mode (Learn crafting recipes progressively) bool false
# Regroup the items of the same type in the item list.
i3_item_compression (Regroup items of the same type) bool true
# Set the inventory size to common chests size (8*4).
i3_legacy_inventory (Legacy inventory size) bool false

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,10 +1,10 @@
local http = ... local http = ...
local make_fs, get_inventory_fs = i3.files.gui() local make_fs, get_inventory_fs = i3.files.gui()
IMPORT("sorter", "sort_inventory", "play_sound") IMPORT("gmatch", "split")
IMPORT("S", "err", "fmt", "reg_items")
IMPORT("sorter", "sort_inventory")
IMPORT("sort", "concat", "copy", "insert", "remove") IMPORT("sort", "concat", "copy", "insert", "remove")
IMPORT("get_player_by_name", "add_hud_waypoint", "init_hud_notif")
IMPORT("gmatch", "split", "S", "err", "fmt", "reg_items", "pos_to_str")
IMPORT("true_str", "true_table", "is_str", "is_func", "is_table", "clean_name") IMPORT("true_str", "true_table", "is_str", "is_func", "is_table", "clean_name")
function i3.register_craft_type(name, def) function i3.register_craft_type(name, def)
@ -196,7 +196,7 @@ end
i3.new_tab("inventory", { i3.new_tab("inventory", {
description = S"Inventory", description = S"Inventory",
formspec = get_inventory_fs, formspec = get_inventory_fs,
slots = true, fields = i3.files.fields(),
}) })
function i3.remove_tab(name) function i3.remove_tab(name)
@ -204,10 +204,10 @@ function i3.remove_tab(name)
return err "i3.remove_tab: tab name missing" return err "i3.remove_tab: tab name missing"
end end
for i = #i3.tabs, 2, -1 do for i, def in ipairs(i3.tabs) do
local def = i3.tabs[i] if name == def.name then
if def and name == def.name then
remove(i3.tabs, i) remove(i3.tabs, i)
break
end end
end end
end end
@ -315,34 +315,16 @@ function i3.hud_notif(name, msg, img)
end end
local data = i3.data[name] local data = i3.data[name]
if not data then if not data then
return err "i3.hud_notif: no player data initialized" return err "i3.hud_notif: no player data initialized"
end end
local player = get_player_by_name(name) data.show_hud = true
if not player then return end data.hud_msg = msg
local max_y = -125
local def = { if img then
show = true, data.hud_img = fmt("%s^[resize:16x16", img)
max = {x = -330, y = max_y},
hud_msg = msg,
hud_img = img and fmt("%s^[resize:64x64", img) or nil,
hud_timer = 0,
elems = init_hud_notif(player),
}
insert(data.hud.notifs, def)
play_sound(name, "i3_achievement", 1.0)
local nb_notifs = #data.hud.notifs
for i = 1, nb_notifs - 1 do
local notif = data.hud.notifs[i]
if notif then
notif.show = true
notif.max.y = ((nb_notifs - i) + 1) * max_y
notif.hud_timer = 0.5 * (nb_notifs - i)
end
end end
end end
@ -362,7 +344,7 @@ end
i3.add_sorting_method("alphabetical", { i3.add_sorting_method("alphabetical", {
description = S"Sort items by name (A-Z)", description = S"Sort items by name (A-Z)",
func = function(list, data) func = function(list, data)
sorter(list, data, 1) sorter(list, data.reverse_sorting, 1)
return list return list
end end
}) })
@ -370,130 +352,7 @@ i3.add_sorting_method("alphabetical", {
i3.add_sorting_method("numerical", { i3.add_sorting_method("numerical", {
description = S"Sort items by number of items per stack", description = S"Sort items by number of items per stack",
func = function(list, data) func = function(list, data)
sorter(list, data, 2) sorter(list, data.reverse_sorting, 2)
return list return list
end, end,
}) })
function i3.add_waypoint(name, def)
if not true_str(name) then
return err "i3.add_waypoint: name missing"
elseif not true_table(def) then
return err "i3.add_waypoint: definition missing"
elseif not true_str(def.player) then
return err "i3.add_waypoint: player name missing"
end
local data = i3.data[def.player]
if not data then
return err "i3.add_waypoint: no player data initialized"
end
local player = get_player_by_name(def.player)
local id = player and add_hud_waypoint(player, name, def.pos, def.color, def.image) or nil
insert(data.waypoints, {
name = name,
pos = pos_to_str(def.pos, 1),
color = def.color,
image = def.image,
id = id,
})
if data.subcat == 5 then
data.scrbar_inv += 1000
end
i3.set_fs(player)
end
function i3.remove_waypoint(player_name, name)
if not true_str(player_name) then
return err "i3.remove_waypoint: player name missing"
elseif not true_str(name) then
return err "i3.remove_waypoint: waypoint name missing"
end
local data = i3.data[player_name]
if not data then
return err "i3.remove_waypoint: no player data initialized"
end
local player = get_player_by_name(player_name)
for i = #data.waypoints, 1, -1 do
local waypoint = data.waypoints[i]
if waypoint and name == waypoint.name then
if player then
player:hud_remove(waypoint.id)
end
remove(data.waypoints, i)
end
end
i3.set_fs(player)
end
function i3.get_waypoints(player_name)
if not true_str(player_name) then
return err "i3.get_waypoints: player name missing"
end
local data = i3.data[player_name]
if not data then
return err "i3.get_waypoints: no player data initialized"
end
return data.waypoints
end
function i3.new_minitab(name, def)
if #i3.minitabs == 6 then
return err "i3.new_minitab: limit reached (6)"
elseif not true_str(name) then
return err "i3.new_minitab: name missing"
elseif not true_table(def) then
return err "i3.new_minitab: definition missing"
end
def.name = name
insert(i3.minitabs, def)
end
function i3.remove_minitab(name)
if not true_str(name) then
return err "i3.remove_minitab: name missing"
end
for i = #i3.minitabs, 2, -1 do
local v = i3.minitabs[i]
if v and v.name == name then
remove(i3.minitabs, i)
end
end
end
i3.new_minitab("all", {
description = "All",
sorter = function()
return true
end
})
i3.new_minitab("nodes", {
description = "Nodes",
sorter = function(item)
return core.registered_nodes[item]
end
})
i3.new_minitab("items", {
description = "Items",
sorter = function(item)
return core.registered_craftitems[item] or core.registered_tools[item]
end
})

View File

@ -1,8 +1,8 @@
local replacements = {fuel = {}} local replacements = {fuel = {}}
local http = ... local http = ...
IMPORT("copy", "insert", "sort", "match", "sub") IMPORT("maxn", "copy", "insert", "sort", "match", "sub")
IMPORT("true_str", "is_table", "valid_item", "table_merge", "table_replace", "table_eq") IMPORT("true_str", "is_table", "valid_item", "table_merge", "table_replace", "rcp_eq")
IMPORT("fmt", "reg_items", "reg_aliases", "reg_nodes", "is_cube", "get_cube", "ItemStack") IMPORT("fmt", "reg_items", "reg_aliases", "reg_nodes", "is_cube", "get_cube", "ItemStack")
IMPORT("is_group", "extract_groups", "item_has_groups", "groups_to_items", "get_group_stereotype") IMPORT("is_group", "extract_groups", "item_has_groups", "groups_to_items", "get_group_stereotype")
@ -56,6 +56,7 @@ local function cache_groups(group, groups)
if c > 1 then if c > 1 then
sprite = sprite:gsub("WxH", px .. "x" .. px * c) sprite = sprite:gsub("WxH", px .. "x" .. px * c)
i3.groups[group].sprite = sprite i3.groups[group].sprite = sprite
i3.groups[group].count = c i3.groups[group].count = c
end end
@ -178,7 +179,17 @@ local function cache_recipes(item)
_recipes[#recipes + 1 - k] = v _recipes[#recipes + 1 - k] = v
end end
local shift = 0
local size_rpl = maxn(replacements[item])
local size_rcp = #_recipes
if size_rpl > size_rcp then
shift = size_rcp - size_rpl
end
for k, v in pairs(replacements[item]) do for k, v in pairs(replacements[item]) do
k += shift
if _recipes[k] then if _recipes[k] then
_recipes[k].replacements = v _recipes[k].replacements = v
end end
@ -238,7 +249,11 @@ local old_clear_craft = core.clear_craft
core.clear_craft = function(def) core.clear_craft = function(def)
old_clear_craft(def) old_clear_craft(def)
-- TODO: hide in crafting guide if true_str(def) then
return -- TODO
elseif is_table(def) then
return -- TODO
end
end end
local function resolve_aliases(hash) local function resolve_aliases(hash)
@ -260,7 +275,7 @@ local function resolve_aliases(hash)
local rcp_new = copy(i3.recipes_cache[newname][j]) local rcp_new = copy(i3.recipes_cache[newname][j])
rcp_new.output = oldname rcp_new.output = oldname
if table_eq(rcp_old, rcp_new) then if rcp_eq(rcp_old, rcp_new) then
similar = true similar = true
break break
end end

View File

@ -1,11 +1,12 @@
local http, storage = ... local http, storage = ...
local init_bags = i3.files.bags() local init_bags = i3.files.bags()
local fill_caches = i3.files.caches(http) local fill_caches = i3.files.caches(http)
local init_detached = i3.files.detached()
local init_hud = i3.files.hud() local init_hud = i3.files.hud()
local set_fs = i3.set_fs local set_fs = i3.set_fs
IMPORT("slz", "min", "insert", "copy", "ItemStack") IMPORT("slz", "min", "insert", "copy", "ItemStack")
IMPORT("spawn_item", "reset_data", "get_detached_inv", "play_sound", "update_inv_size") IMPORT("spawn_item", "reset_data", "get_detached_inv")
core.register_on_player_hpchange(function(player, hpchange) core.register_on_player_hpchange(function(player, hpchange)
local name = player:get_player_name() local name = player:get_player_name()
@ -65,89 +66,16 @@ core.register_on_player_inventory_action(function(player, _, _, info)
end end
end) end)
if core.global_exists"armor" then if core.global_exists("armor") then
i3.modules.armor = true i3.modules.armor = true
armor:register_on_update(set_fs)
local group_indexes = {
{"armor_head", "i3_heavy_helmet"},
{"armor_torso", "i3_heavy_armor"},
{"armor_legs", "i3_heavy_leggings"},
{"armor_feet", "i3_heavy_boots"},
{"armor_shield", "i3_heavy_shield"},
}
local function check_group(def, group)
return def.groups[group] and def.groups[group] > 0
end
armor:register_on_equip(function(player, idx, stack)
local _, armor_inv = armor:get_valid_player(player, "3d_armor")
local def = stack:get_definition()
local name = player:get_player_name()
local data = i3.data[name]
for i, v in ipairs(group_indexes) do
local group, sound = unpack(v)
local stackname = stack:get_name()
if stackname:find"wood" or stackname:find"stone" or stackname:find"cactus" then
sound = sound:gsub("heavy", "light")
end
if i == idx and check_group(def, group) then
data.armor_allow = sound
return armor:register_on_update(set_fs)
end
end
data.armor_disallow = true
armor_inv:remove_item("armor", stack)
end)
armor:register_on_update(function(player)
local _, armor_inv = armor:get_valid_player(player, "3d_armor")
if not armor_inv then return end
for i = 1, 5 do
local stack = armor_inv:get_stack("armor", i)
local def = stack:get_definition()
for j, v in ipairs(group_indexes) do
local group = v[1]
if check_group(def, group) and i ~= j then
armor_inv:set_stack("armor", i, armor_inv:get_stack("armor", j))
armor_inv:set_stack("armor", j, stack)
return play_sound(player:get_player_name(), "i3_cannot", 0.8)
end
end
end
end)
core.register_on_player_inventory_action(function(player, action, _, info)
if action ~= "take" then return end
local name = player:get_player_name()
local data = i3.data[name]
if data.armor_disallow then
local inv = player:get_inventory()
inv:set_stack("main", info.index, info.stack)
data.armor_disallow = nil
play_sound(name, "i3_cannot", 0.8)
elseif data.armor_allow then
play_sound(name, data.armor_allow, 0.8)
data.armor_allow = nil
end
end)
end end
if core.global_exists"skins" then if core.global_exists("skins") then
i3.modules.skins = true i3.modules.skins = true
end end
if core.global_exists"awards" then if core.global_exists("awards") then
i3.modules.awards = true i3.modules.awards = true
core.register_on_craft(function(_, player) core.register_on_craft(function(_, player)
@ -203,15 +131,6 @@ local function init_data(player, info)
i3.data[name] = i3.data[name] or {} i3.data[name] = i3.data[name] or {}
local data = i3.data[name] local data = i3.data[name]
for k, v in pairs(i3.default_data) do
local val = data[k]
if val == nil then
val = v
end
data[k] = val
end
data.player_name = name data.player_name = name
data.filter = "" data.filter = ""
data.pagenum = 1 data.pagenum = 1
@ -219,8 +138,13 @@ local function init_data(player, info)
data.items = i3.init_items data.items = i3.init_items
data.items_raw = i3.init_items data.items_raw = i3.init_items
data.favs = {} data.favs = {}
data.sort = "alphabetical"
data.show_setting = "home" data.show_setting = "home"
data.crafting_counts = {} data.ignore_hotbar = false
data.auto_sorting = false
data.reverse_sorting = false
data.inv_compress = true
data.export_counts = {}
data.tab = 1 data.tab = 1
data.itab = 1 data.itab = 1
data.subcat = 1 data.subcat = 1
@ -228,7 +152,8 @@ local function init_data(player, info)
data.lang_code = get_lang_code(info) data.lang_code = get_lang_code(info)
data.fs_version = info.formspec_version data.fs_version = info.formspec_version
update_inv_size(player, data) local inv = player:get_inventory()
inv:set_size("main", i3.settings.inv_size)
core.after(0, set_fs, player) core.after(0, set_fs, player)
end end
@ -261,6 +186,7 @@ insert(core.registered_on_joinplayers, 1, function(player)
init_data(player, info) init_data(player, info)
init_bags(player) init_bags(player)
init_detached(player)
init_hud(player) init_hud(player)
end) end)

View File

@ -1,8 +1,8 @@
local vec = vector.new
local ItemStack = ItemStack local ItemStack = ItemStack
local loadstring = loadstring local loadstring = loadstring
local reg_items = core.registered_items local reg_items = core.registered_items
local translate = core.get_translated_string local translate = core.get_translated_string
local vec_new, vec_add, vec_mul = vector.new, vector.add, vector.multiply
local sort, concat, insert = table.sort, table.concat, table.insert local sort, concat, insert = table.sort, table.concat, table.insert
local min, floor, ceil = math.min, math.floor, math.ceil local min, floor, ceil = math.min, math.floor, math.ceil
local fmt, find, match, gmatch, sub, split, lower, upper = local fmt, find, match, gmatch, sub, split, lower, upper =
@ -80,7 +80,7 @@ end
local function utf8_len(str) local function utf8_len(str)
local c = 0 local c = 0
for _ in str:gmatch"[%z\1-\127\194-\244][\128-\191]*" do -- Arguably working duct-tape code for _ in str:gmatch"([%z\1-\127\194-\244][\128-\191]*)" do -- Arguably working duct-tape code
c++ c++
end end
@ -216,22 +216,18 @@ local function array_diff(t1, t2)
return diff return diff
end end
local function table_eq(t1, t2) local function rcp_eq(rcp, rcp2)
local ty1, ty2 = type(t1), type(t2) if rcp.type ~= rcp2.type then return end
if ty1 ~= ty2 then return end if rcp.width ~= rcp2.width then return end
if #rcp.items ~= #rcp2.items then return end
if rcp.output ~= rcp2.output then return end
if ty1 ~= "table" and ty2 ~= "table" then for i, item in pairs(rcp.items) do
return t1 == t2 if item ~= rcp2.items[i] then return end
end end
for k, v in pairs(t1) do for i, item in pairs(rcp2.items) do
local v2 = t2[k] if item ~= rcp.items[i] then return end
if v2 == nil or not table_eq(v, v2) then return end
end
for k, v in pairs(t2) do
local v1 = t1[k]
if v1 == nil or not table_eq(v1, v) then return end
end end
return true return true
@ -327,12 +323,8 @@ local function apply_recipe_filters(recipes, player)
return recipes return recipes
end end
local function recipe_filter_set()
return next(i3.recipe_filters)
end
local function compression_active(data) local function compression_active(data)
return data.collapse and not recipe_filter_set() and data.filter == "" return i3.settings.item_compression and not next(i3.recipe_filters) and data.filter == ""
end end
local function compressible(item, data) local function compressible(item, data)
@ -360,8 +352,13 @@ local function sort_by_category(data)
for i = 1, #items do for i = 1, #items do
local item = items[i] local item = items[i]
local tab = i3.minitabs[data.itab] local to_add = true
local to_add = tab.sorter(item, data)
if data.itab == 2 then
to_add = core.registered_nodes[item]
elseif data.itab == 3 then
to_add = core.registered_craftitems[item] or core.registered_tools[item]
end
if to_add then if to_add then
insert(new, item) insert(new, item)
@ -375,7 +372,7 @@ local function spawn_item(player, stack)
local dir = player:get_look_dir() local dir = player:get_look_dir()
local ppos = player:get_pos() local ppos = player:get_pos()
ppos.y = ppos.y + player:get_properties().eye_height ppos.y = ppos.y + player:get_properties().eye_height
local look_at = ppos + dir local look_at = vec_add(ppos, vec_mul(dir, 1))
core.add_item(look_at, stack) core.add_item(look_at, stack)
end end
@ -408,61 +405,39 @@ local function get_stack(player, stack)
end end
end end
local function get_group_items(name)
local groups = extract_groups(name)
return i3.groups[name:sub(7)].items or groups_to_items(groups)
end
local function craft_stack(player, data, craft_rcp) local function craft_stack(player, data, craft_rcp)
local inv = player:get_inventory() local inv = player:get_inventory()
local rcp_usg = craft_rcp and "recipe" or "usage" local rcp_usg = craft_rcp and "recipe" or "usage"
local rcp_def = rcp_usg == "recipe" and data.recipes[data.rnum] or data.usages[data.unum]
local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output
output = ItemStack(output) output = ItemStack(output)
local stackname, stackcount, stackmax = output:get_name(), output:get_count(), output:get_stack_max() local stackname, stackcount, stackmax = output:get_name(), output:get_count(), output:get_stack_max()
local scrbar_val = data[fmt("scrbar_%s", craft_rcp and "rcp" or "usg")] or 1 local scrbar_val = data[fmt("scrbar_%s", craft_rcp and "rcp" or "usg")] or 1
for name, count in pairs(data.crafting_counts[rcp_usg].rcp) do for name, count in pairs(data.export_counts[rcp_usg].rcp) do
local items = {[name] = count} local items = {[name] = count}
if is_group(name) then if is_group(name) then
items = {} items = {}
local item_groups = get_group_items(name) local groups = extract_groups(name)
local groupname = name:sub(7)
local item_groups = i3.groups[groupname].items or groups_to_items(groups)
local remaining = count local remaining = count
for _, item in ipairs(item_groups) do for _, item in ipairs(item_groups) do
for _name, _count in pairs(data.crafting_counts[rcp_usg].inv) do for _name, _count in pairs(data.export_counts[rcp_usg].inv) do
if item == _name and remaining > 0 then if item == _name and remaining > 0 then
local c = min(remaining, _count) local c = min(remaining, _count)
items[item] = c items[item] = c
remaining -= c remaining -= c
end
if remaining == 0 then break end
end end
if remaining == 0 then break end
end
end end
end end
for item, v in pairs(items) do for k, v in pairs(items) do
for _ = 1, v * scrbar_val do inv:remove_item("main", fmt("%s %s", k, v * scrbar_val))
inv:remove_item("main", item)
for _, pair in ipairs(rcp_def.replacements or {}) do
local old_name, new_name = unpack(pair)
if is_group(old_name) then
local item_groups = get_group_items(old_name)
for _, it in ipairs(item_groups) do
if item == it then
get_stack(player, ItemStack(new_name))
end
end
elseif item == old_name then
get_stack(player, ItemStack(new_name))
end
end
end
end end
end end
@ -487,24 +462,35 @@ local function safe_teleport(player, pos)
play_sound(name, "i3_teleport", 0.8) play_sound(name, "i3_teleport", 0.8)
local vel = player:get_velocity() local vel = player:get_velocity()
player:add_velocity(-vel) player:add_velocity(vec_mul(vel, -1))
local p = vec(pos) local p = vec_new(pos)
p.y += 0.25 p.y += 0.25
player:set_pos(p) player:set_pos(p)
end end
local function sorter(inv, data, mode) local function get_sorting_idx(name)
local idx = 1
for i, def in ipairs(i3.sorting_methods) do
if name == def.name then
idx = i
end
end
return idx
end
local function sorter(inv, reverse, mode)
sort(inv, function(a, b) sort(inv, function(a, b)
if mode == 1 then if mode == 1 then
a = translate(data.lang_code, a:get_short_description()) a, b = a:get_name(), b:get_name()
b = translate(data.lang_code, b:get_short_description())
else else
a, b = a:get_count(), b:get_count() a, b = a:get_count(), b:get_count()
end end
if data.reverse_sorting then if reverse then
return a > b return a > b
end end
@ -571,15 +557,43 @@ local function compress_items(list, start_i)
return new_inv return new_inv
end end
local function drop_items(player, inv, list, start_i, rej, remove)
for i = start_i, #list do
local stack = list[i]
local name = stack:get_name()
for _, it in ipairs(rej) do
if name == it then
if not remove then
spawn_item(player, stack)
end
inv:set_stack("main", i, ItemStack(""))
end
end
end
return inv:get_list"main"
end
local function sort_inventory(player, data) local function sort_inventory(player, data)
local inv = player:get_inventory() local inv = player:get_inventory()
local list = inv:get_list"main" local list = inv:get_list"main"
local size = inv:get_size"main" local size = inv:get_size"main"
local start_i = data.ignore_hotbar and (data.hotbar_len + 1) or 1 local start_i = data.ignore_hotbar and (i3.settings.hotbar_len + 1) or 1
list = data.inv_compress and compress_items(list, start_i) or pre_sorting(list, start_i) if true_table(data.drop_items) then
list = drop_items(player, inv, list, start_i, data.drop_items, true)
end
local new_inv = i3.sorting_methods[data.sort].func(list, data) if data.inv_compress then
list = compress_items(list, start_i)
else
list = pre_sorting(list, start_i)
end
local idx = get_sorting_idx(data.sort)
local new_inv = i3.sorting_methods[idx].func(list, data)
if not new_inv then return end if not new_inv then return end
if not data.ignore_hotbar then if not data.ignore_hotbar then
@ -603,11 +617,10 @@ local function reset_data(data)
data.scrbar_usg = 1 data.scrbar_usg = 1
data.query_item = nil data.query_item = nil
data.enable_search = nil data.enable_search = nil
data.goto_page = nil
data.recipes = nil data.recipes = nil
data.usages = nil data.usages = nil
data.crafting_rcp = nil data.export_rcp = nil
data.crafting_usg = nil data.export_usg = nil
data.alt_items = nil data.alt_items = nil
data.confirm_trash = nil data.confirm_trash = nil
data.show_settings = nil data.show_settings = nil
@ -619,54 +632,17 @@ local function reset_data(data)
end end
end end
local function add_hud_waypoint(player, name, pos, color, image) local function add_hud_waypoint(player, name, pos, color)
return player:hud_add { return player:hud_add {
hud_elem_type = image and "image_waypoint" or "waypoint", hud_elem_type = "waypoint",
name = name, name = name,
text = image or "m", text = "m",
scale = {x = 5, y = 5},
world_pos = pos, world_pos = pos,
number = color, number = color,
image = image,
z_index = -300, z_index = -300,
} }
end end
local function init_hud_notif(player)
return {
bg = player:hud_add {
hud_elem_type = "image",
position = {x = 0, y = 1},
offset = {x = 10, y = 0},
alignment = {x = 1, y = 1},
scale = {x = 0.6, y = 0.6},
text = "i3_bg_notif.png",
z_index = 0xDEAD,
},
img = player:hud_add {
hud_elem_type = "image",
position = {x = 0, y = 1},
offset = {x = 20, y = 20},
alignment = {x = 1, y = 1},
scale = {x = 1, y = 1},
text = "",
z_index = 0xDEAD,
},
text = player:hud_add {
hud_elem_type = "text",
position = {x = 0, y = 1},
offset = {x = 100, y = 40},
alignment = {x = 1, y = 1},
number = 0xffffff,
text = "",
z_index = 0xDEAD,
style = 1,
}
}
end
local function get_detached_inv(name, player_name) local function get_detached_inv(name, player_name)
return core.get_inventory { return core.get_inventory {
type = "detached", type = "detached",
@ -674,20 +650,6 @@ local function get_detached_inv(name, player_name)
} }
end end
local function update_inv_size(player, data)
data.hotbar_len = data.legacy_inventory and 8 or 9
data.inv_size = 4 * data.hotbar_len
local inv = player:get_inventory()
inv:set_size("main", data.inv_size)
player:hud_set_hotbar_itemcount(data.hotbar_len)
core.after(0, function()
player:hud_set_hotbar_image(data.legacy_inventory and "gui_hotbar.png" or "i3_hotbar.png")
end)
end
-- Much faster implementation of `unpack` -- Much faster implementation of `unpack`
local function createunpack(n) local function createunpack(n)
local ret = {"local t = ... return "} local ret = {"local t = ... return "}
@ -726,8 +688,8 @@ local _ = {
sorter = sorter, sorter = sorter,
get_recipes = get_recipes, get_recipes = get_recipes,
sort_inventory = sort_inventory, sort_inventory = sort_inventory,
get_sorting_idx = get_sorting_idx,
sort_by_category = sort_by_category, sort_by_category = sort_by_category,
recipe_filter_set = recipe_filter_set,
apply_recipe_filters = apply_recipe_filters, apply_recipe_filters = apply_recipe_filters,
-- Type checks -- Type checks
@ -752,7 +714,6 @@ local _ = {
play_sound = play_sound, play_sound = play_sound,
reset_data = reset_data, reset_data = reset_data,
safe_teleport = safe_teleport, safe_teleport = safe_teleport,
init_hud_notif = init_hud_notif,
add_hud_waypoint = add_hud_waypoint, add_hud_waypoint = add_hud_waypoint,
-- Core functions -- Core functions
@ -771,7 +732,6 @@ local _ = {
-- Inventory -- Inventory
get_stack = get_stack, get_stack = get_stack,
craft_stack = craft_stack, craft_stack = craft_stack,
update_inv_size = update_inv_size,
get_detached_inv = get_detached_inv, get_detached_inv = get_detached_inv,
get_bag_description = get_bag_description, get_bag_description = get_bag_description,
create_inventory = core.create_detached_inventory, create_inventory = core.create_detached_inventory,
@ -813,12 +773,11 @@ local _ = {
is_table = is_table, is_table = is_table,
table_merge = table_merge, table_merge = table_merge,
table_replace = table_replace, table_replace = table_replace,
table_eq = table_eq, rcp_eq = rcp_eq,
array_diff = array_diff, array_diff = array_diff,
-- Math -- Math
round = round, round = round,
abs = math.abs,
min = math.min, min = math.min,
max = math.max, max = math.max,
ceil = math.ceil, ceil = math.ceil,
@ -826,8 +785,12 @@ local _ = {
random = math.random, random = math.random,
-- Vectors -- Vectors
vec = vector.new, vec_new = vector.new,
vec_add = vector.add,
vec_sub = vector.subtract,
vec_mul = vector.multiply,
vec_round = vector.round, vec_round = vector.round,
vec_eq = vector.equals,
} }
function i3.get(...) function i3.get(...)

View File

@ -1,5 +1,5 @@
local set_fs = i3.set_fs local set_fs = i3.set_fs
IMPORT("play_sound", "create_inventory") IMPORT("fmt", "play_sound", "create_inventory")
local trash = create_inventory("i3_trash", { local trash = create_inventory("i3_trash", {
allow_put = function(_, _, _, stack) allow_put = function(_, _, _, stack)
@ -10,9 +10,6 @@ local trash = create_inventory("i3_trash", {
inv:set_list(listname, {}) inv:set_list(listname, {})
local name = player:get_player_name() local name = player:get_player_name()
local data = i3.data[name]
data.armor_allow = nil
play_sound(name, "i3_trash", 1.0) play_sound(name, "i3_trash", 1.0)
if not core.is_creative_enabled(name) then if not core.is_creative_enabled(name) then
@ -22,3 +19,15 @@ local trash = create_inventory("i3_trash", {
}) })
trash:set_size("main", 1) trash:set_size("main", 1)
local function init_detached(player)
local name = player:get_player_name()
local output_rcp = create_inventory(fmt("i3_output_rcp_%s", name), {}, name)
output_rcp:set_size("main", 1)
local output_usg = create_inventory(fmt("i3_output_usg_%s", name), {}, name)
output_usg:set_size("main", 1)
end
return init_detached

View File

@ -1,22 +1,27 @@
local set_fs = i3.set_fs local set_fs = i3.set_fs
IMPORT("min", "max", "vec_round") IMPORT("vec_eq", "vec_round")
IMPORT("reg_items", "reg_aliases") IMPORT("reg_items", "reg_aliases")
IMPORT("S", "random", "translate", "ItemStack")
IMPORT("sort", "copy", "insert", "remove", "indexof") IMPORT("sort", "copy", "insert", "remove", "indexof")
IMPORT("S", "random", "translate", "compressible", "ItemStack")
IMPORT("fmt", "find", "match", "sub", "lower", "split", "toupper") IMPORT("fmt", "find", "match", "sub", "lower", "split", "toupper")
IMPORT("valid_item", "get_stack", "craft_stack", "clean_name", "check_privs", "safe_teleport")
IMPORT("msg", "is_fav", "pos_to_str", "str_to_pos", "add_hud_waypoint", "play_sound", "reset_data") IMPORT("msg", "is_fav", "pos_to_str", "str_to_pos", "add_hud_waypoint", "play_sound", "reset_data")
IMPORT("search", "sort_inventory", "sort_by_category", "get_recipes", "get_detached_inv", "update_inv_size") IMPORT("search", "get_sorting_idx", "sort_inventory", "sort_by_category", "get_recipes", "get_detached_inv")
IMPORT("valid_item", "get_stack", "craft_stack", "clean_name", "compressible", "check_privs", "safe_teleport")
local function inv_fields(player, data, fields) local function inv_fields(player, data, fields)
local name = data.player_name local name = data.player_name
local inv = player:get_inventory() local inv = player:get_inventory()
local sb_inv = fields.scrbar_inv
if fields.dd_sorting_method then if sb_inv and sub(sb_inv, 1, 3) == "CHG" then
data.sort = tonumber(fields.dd_sorting_method) data.scrbar_inv = tonumber(match(sb_inv, "%d+"))
elseif fields.sb_font_size then return
data.font_size = tonumber(fields.sb_font_size:match"-?%d+$") end
if fields.drop_items then
local items = split(fields.drop_items, ",")
data.drop_items = items
end end
for field in pairs(fields) do for field in pairs(fields) do
@ -32,20 +37,12 @@ local function inv_fields(player, data, fields)
data[str] = true data[str] = true
end end
if str == "legacy_inventory" then
update_inv_size(player, data)
elseif str == "collapse" then
search(data)
end
elseif sub(field, 1, 8) == "setting_" then elseif sub(field, 1, 8) == "setting_" then
data.show_setting = match(field, "_(%w+)$") data.show_setting = match(field, "_(%w+)$")
elseif sub(field, 1, 9) == "skin_btn_" then elseif sub(field, 1, 9) == "skin_btn_" then
local id = tonumber(field:match("%d+")) local id = tonumber(field:match("%d+"))
local _skins = skins.get_skinlist_for_player(name) local _skins = skins.get_skinlist_for_player(name)
play_sound(name, "i3_skin_change", 0.6)
skins.set_player_skin(player, _skins[id]) skins.set_player_skin(player, _skins[id])
elseif find(field, "waypoint_%d+") then elseif find(field, "waypoint_%d+") then
@ -97,7 +94,6 @@ local function inv_fields(player, data, fields)
data.show_settings = nil data.show_settings = nil
data.waypoint_see = nil data.waypoint_see = nil
data.bag_rename = nil data.bag_rename = nil
data.goto_page = nil
if data.filter == "" then if data.filter == "" then
data.enable_search = nil data.enable_search = nil
@ -132,6 +128,20 @@ local function inv_fields(player, data, fields)
elseif fields.sort then elseif fields.sort then
sort_inventory(player, data) sort_inventory(player, data)
elseif fields.prev_sort or fields.next_sort then
local idx = get_sorting_idx(data.sort)
local tot = #i3.sorting_methods
idx -= (fields.prev_sort and 1 or -1)
if idx > tot then
idx = 1
elseif idx == 0 then
idx = tot
end
data.sort = i3.sorting_methods[idx].name
elseif fields.home then elseif fields.home then
if not data.home then if not data.home then
return msg(name, "No home set") return msg(name, "No home set")
@ -178,9 +188,9 @@ local function inv_fields(player, data, fields)
local pos = player:get_pos() local pos = player:get_pos()
for _, v in ipairs(data.waypoints) do for _, v in ipairs(data.waypoints) do
if vec_round(pos) == vec_round(str_to_pos(v.pos)) then if vec_eq(vec_round(pos), vec_round(str_to_pos(v.pos))) then
play_sound(name, "i3_cannot", 0.8) play_sound(name, "i3_cannot", 0.8)
return msg(name, S"You already have set a waypoint at this position") return msg(name, S"You already set a waypoint at this position")
end end
end end
@ -205,6 +215,8 @@ local function inv_fields(player, data, fields)
elseif fields.hide_debug_grid then elseif fields.hide_debug_grid then
data.hide_debug_grid = not data.hide_debug_grid data.hide_debug_grid = not data.hide_debug_grid
end end
return set_fs(player)
end end
local function select_item(player, data, fields) local function select_item(player, data, fields)
@ -286,22 +298,22 @@ local function select_item(player, data, fields)
if item == data.query_item then return end if item == data.query_item then return end
local recipes, usages = get_recipes(player, item) local recipes, usages = get_recipes(player, item)
data.query_item = item data.query_item = item
data.recipes = recipes data.recipes = recipes
data.usages = usages data.usages = usages
data.rnum = 1 data.rnum = 1
data.unum = 1 data.unum = 1
data.scrbar_rcp = 1 data.scrbar_rcp = 1
data.scrbar_usg = 1 data.scrbar_usg = 1
data.crafting_rcp = nil data.export_rcp = nil
data.crafting_usg = nil data.export_usg = nil
end end
end end
local function rcp_fields(player, data, fields) local function rcp_fields(player, data, fields)
local sb_rcp, sb_usg = fields.scrbar_rcp, fields.scrbar_usg local sb_rcp, sb_usg = fields.scrbar_rcp, fields.scrbar_usg
if not data.hide_tabs and fields.filter and fields.filter == "" then if data.filter == "" then
data.enable_search = nil data.enable_search = nil
end end
@ -312,11 +324,8 @@ local function rcp_fields(player, data, fields)
data.query_item = nil data.query_item = nil
elseif fields.enable_search then elseif fields.enable_search then
if data.hide_tabs then data.enable_search = true
data.enable_search = not data.enable_search return set_fs(player)
else
data.enable_search = true
end
elseif fields.filter and (fields.key_enter_field == "filter" or fields.search) then elseif fields.filter and (fields.key_enter_field == "filter" or fields.search) then
if fields.filter == "" then if fields.filter == "" then
@ -336,14 +345,6 @@ local function rcp_fields(player, data, fields)
sort_by_category(data) sort_by_category(data)
end end
elseif fields.pagenum then
data.goto_page = not data.goto_page
elseif fields.goto_page then
local pagenum = tonumber(fields.goto_page)
data.pagenum = max(1, min(data.pagemax, pagenum or data.pagenum))
data.goto_page = nil
elseif fields.prev_page or fields.next_page then elseif fields.prev_page or fields.next_page then
if data.pagemax == 1 then return end if data.pagemax == 1 then return end
data.pagenum -= (fields.prev_page and 1 or -1) data.pagenum -= (fields.prev_page and 1 or -1)
@ -367,13 +368,13 @@ local function rcp_fields(player, data, fields)
elseif fields.prev_recipe or fields.next_recipe then elseif fields.prev_recipe or fields.next_recipe then
local num = data.rnum + (fields.prev_recipe and -1 or 1) local num = data.rnum + (fields.prev_recipe and -1 or 1)
data.rnum = data.recipes[num] and num or (fields.prev_recipe and #data.recipes or 1) data.rnum = data.recipes[num] and num or (fields.prev_recipe and #data.recipes or 1)
data.crafting_rcp = nil data.export_rcp = nil
data.scrbar_rcp = 1 data.scrbar_rcp = 1
elseif fields.prev_usage or fields.next_usage then elseif fields.prev_usage or fields.next_usage then
local num = data.unum + (fields.prev_usage and -1 or 1) local num = data.unum + (fields.prev_usage and -1 or 1)
data.unum = data.usages[num] and num or (fields.prev_usage and #data.usages or 1) data.unum = data.usages[num] and num or (fields.prev_usage and #data.usages or 1)
data.crafting_usg = nil data.export_usg = nil
data.scrbar_usg = 1 data.scrbar_usg = 1
elseif fields.fav then elseif fields.fav then
@ -385,17 +386,17 @@ local function rcp_fields(player, data, fields)
remove(data.favs, fav) remove(data.favs, fav)
end end
elseif fields.crafting_rcp or fields.crafting_usg then elseif fields.export_rcp or fields.export_usg then
if fields.crafting_rcp then if fields.export_rcp then
data.crafting_rcp = not data.crafting_rcp data.export_rcp = not data.export_rcp
if not data.crafting_rcp then if not data.export_rcp then
data.scrbar_rcp = 1 data.scrbar_rcp = 1
end end
else else
data.crafting_usg = not data.crafting_usg data.export_usg = not data.export_usg
if not data.crafting_usg then if not data.export_usg then
data.scrbar_usg = 1 data.scrbar_usg = 1
end end
end end
@ -408,10 +409,10 @@ local function rcp_fields(player, data, fields)
craft_stack(player, data, fields.craft_rcp) craft_stack(player, data, fields.craft_rcp)
if fields.craft_rcp then if fields.craft_rcp then
data.crafting_rcp = nil data.export_rcp = nil
data.scrbar_rcp = 1 data.scrbar_rcp = 1
else else
data.crafting_usg = nil data.export_usg = nil
data.scrbar_usg = 1 data.scrbar_usg = 1
end end
else else
@ -423,28 +424,23 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name() local name = player:get_player_name()
if formname == "i3_outdated" then if formname == "i3_outdated" then
return false, core.kick_player(name, S"Your Minetest client needs updating (www.minetest.net)") return false, core.kick_player(name,
S"Come back when your Minetest client is up-to-date (www.minetest.net).")
elseif formname ~= "" then elseif formname ~= "" then
return false return false
end end
-- No-op buttons -- No-op buttons
if fields.player_name or fields.awards or fields.home_pos or fields.no_item or if fields.player_name or fields.awards or fields.home_pos or fields.pagenum or
fields.no_rcp or fields.select_sorting or fields.sort_method or fields.bg_content or fields.no_item or fields.no_rcp or fields.select_sorting or fields.sort_method or
fields.quick_crafting then fields.bg_content then
return false return false
end end
-- print(dump(fields)) --print(dump(fields))
local data = i3.data[name] local data = i3.data[name]
if not data then return end if not data then return end
local sb_inv = fields.scrbar_inv
if sb_inv and sub(sb_inv, 1, 3) == "CHG" then
data.scrbar_inv = tonumber(match(sb_inv, "%d+"))
return
end
for f in pairs(fields) do for f in pairs(fields) do
if sub(f, 1, 4) == "tab_" then if sub(f, 1, 4) == "tab_" then
local tabname = sub(f, 5) local tabname = sub(f, 5)
@ -461,16 +457,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
rcp_fields(player, data, fields) rcp_fields(player, data, fields)
local tab = i3.tabs[data.tab] local tab = i3.tabs[data.tab]
if tab then
if tab.slots then
inv_fields(player, data, fields)
end
if tab.fields then if tab and tab.fields then
local ret = tab.fields(player, data, fields) return true, tab.fields(player, data, fields)
if ret == false then return end
end
end end
return true, set_fs(player) return true, set_fs(player)
end) end)
return inv_fields

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +1,97 @@
IMPORT("max", "ceil", "remove", "str_to_pos") IMPORT("get_connected_players", "str_to_pos", "add_hud_waypoint")
IMPORT("get_connected_players", "add_hud_waypoint")
local function init_hud(player) local function init_hud(player)
local name = player:get_player_name() local name = player:get_player_name()
local data = i3.data[name] local data = i3.data[name]
local wdesc_y = -90
if core.global_exists"hb" then
wdesc_y -= ceil(hb.hudbars_count / 2) * 5
elseif not i3.settings.damage_enabled then
wdesc_y += 15
end
data.hud = { data.hud = {
notifs = {}, bg = player:hud_add {
hud_elem_type = "image",
position = {x = 0.78, y = 1},
alignment = {x = 1, y = 1},
scale = {x = 370, y = 112},
text = "i3_bg.png",
z_index = 0xDEAD,
},
wielditem = player:hud_add { img = player:hud_add {
hud_elem_type = "image",
position = {x = 0.79, y = 1.02},
alignment = {x = 1, y = 1},
scale = {x = 4, y = 4},
text = "",
z_index = 0xDEAD,
},
text = player:hud_add {
hud_elem_type = "text", hud_elem_type = "text",
position = {x = 0.5, y = 1}, position = {x = 0.84, y = 1.04},
offset = {x = 0, y = wdesc_y}, alignment = {x = 1, y = 1},
alignment = {x = 0, y = -1},
number = 0xffffff, number = 0xffffff,
text = "", text = "",
z_index = 0xDEAD, z_index = 0xDEAD,
style = 1, style = 1,
} },
} }
if not i3.settings.legacy_inventory then
core.after(0, function()
player:hud_set_hotbar_itemcount(i3.settings.hotbar_len)
player:hud_set_hotbar_image"i3_hotbar.png"
end)
end
end end
local function get_progress(offset, max_val) local function show_hud(player, data)
local progress = offset * (1 / (max_val - 5)) -- It would better to have an engine function `hud_move` to only need
return 1 - (progress ^ 4) -- 2 calls for the notification's back and forth.
end
local function show_hud(player, data, notif, idx, dt) local hud_info_bg = player:hud_get(data.hud.bg)
local hud_info_bg = player:hud_get(notif.elems.bg) local dt = 0.016
local offset = hud_info_bg.offset
if offset.y < notif.max.y then if hud_info_bg.position.y <= 0.9 then
notif.show = false data.show_hud = false
notif.hud_timer += dt data.hud_timer = (data.hud_timer or 0) + dt
end end
player:hud_change(notif.elems.text, "text", notif.hud_msg) player:hud_change(data.hud.text, "text", data.hud_msg)
if notif.hud_img then if data.hud_img then
player:hud_change(notif.elems.img, "text", notif.hud_img) player:hud_change(data.hud.img, "text", data.hud_img)
end end
if notif.show then if data.show_hud then
local speed = i3.settings.hud_speed * (100 * get_progress(offset.y, notif.max.y)) * dt for _, def in pairs(data.hud) do
for _, def in pairs(notif.elems) do
local hud_info = player:hud_get(def) local hud_info = player:hud_get(def)
player:hud_change(def, "offset", { player:hud_change(def, "position", {
x = hud_info.offset.x, x = hud_info.position.x,
y = hud_info.offset.y - (speed * max(1, (#data.hud.notifs - idx + 1) / 1.45)) y = hud_info.position.y - ((dt / 5) * i3.settings.hud_speed)
}) })
end end
elseif notif.show == false and notif.hud_timer >= i3.settings.hud_timer_max then
local speed = (i3.settings.hud_speed * 2.6) * (100 * get_progress(offset.x, notif.max.x)) * dt
for _, def in pairs(notif.elems) do elseif data.show_hud == false then
local hud_info = player:hud_get(def) if data.hud_timer >= i3.settings.hud_timer_max then
for _, def in pairs(data.hud) do
local hud_info = player:hud_get(def)
player:hud_change(def, "offset", { player:hud_change(def, "position", {
x = hud_info.offset.x - speed, x = hud_info.position.x,
y = hud_info.offset.y y = hud_info.position.y + ((dt / 5) * i3.settings.hud_speed)
}) })
end
if hud_info.offset.x < notif.max.x then if hud_info_bg.position.y >= 1 then
player:hud_remove(def) data.show_hud = nil
remove(data.hud.notifs, idx) data.hud_timer = nil
data.hud_msg = nil
data.hud_img = nil
end end
end end
end end
end end
core.register_globalstep(function(dt) core.register_globalstep(function()
local players = get_connected_players() local players = get_connected_players()
players[0] = #players players[0] = #players
@ -86,45 +99,10 @@ core.register_globalstep(function(dt)
local player = players[i] local player = players[i]
local name = player:get_player_name() local name = player:get_player_name()
local data = i3.data[name] local data = i3.data[name]
if not data then return end
for idx, notif in ipairs(data.hud.notifs) do if data and data.show_hud ~= nil then
if notif.show ~= nil then show_hud(player, data)
show_hud(player, data, notif, idx, dt)
end
end end
local has_text = player:hud_get(data.hud.wielditem).text ~= ""
if not data.wielditem_hud then
if has_text then
player:hud_change(data.hud.wielditem, "text", "")
end
return
end
data.timer = (data.timer or 0) + dt
local wieldidx = player:get_wield_index()
if wieldidx == data.old_wieldidx then
if data.timer >= i3.settings.wielditem_fade_after and has_text then
player:hud_change(data.hud.wielditem, "text", "")
end
return
end
data.timer = 0
data.old_wieldidx = wieldidx
local wielditem = player:get_wielded_item()
local meta = wielditem:get_meta()
local meta_desc = meta:get_string"short_description"
meta_desc = meta_desc:gsub("\27", "")
meta_desc = core.strip_colors(meta_desc)
local desc = meta_desc ~= "" and meta_desc or wielditem:get_short_description()
player:hud_change(data.hud.wielditem, "text", desc:trim())
end end
end) end)
@ -135,7 +113,7 @@ local function init_waypoints(player)
for _, v in ipairs(data.waypoints) do for _, v in ipairs(data.waypoints) do
if not v.hide then if not v.hide then
local id = add_hud_waypoint(player, v.name, str_to_pos(v.pos), v.color, v.image) local id = add_hud_waypoint(player, v.name, str_to_pos(v.pos), v.color)
v.id = id v.id = id
end end
end end

View File

@ -81,10 +81,6 @@ local function _load(path, line, data, t)
if not l then if not l then
local err_line = tonumber(err:match(":(%d+):")) local err_line = tonumber(err:match(":(%d+):"))
if t then
print("err_line", err_line, t[err_line])
end
if line ~= err_line then if line ~= err_line then
return _load(path, err_line, data, t) return _load(path, err_line, data, t)
end end

View File

@ -2,22 +2,11 @@ local set_fs = i3.set_fs
local hud_notif = i3.hud_notif local hud_notif = i3.hud_notif
local POLL_FREQ = 0.25 local POLL_FREQ = 0.25
IMPORT("reg_items", "reg_nodes", "fmt", "table_merge", "array_diff") IMPORT("fmt", "search", "table_merge", "array_diff")
IMPORT("is_group", "extract_groups", "item_has_groups", "apply_recipe_filters", "sort_by_category") IMPORT("is_group", "extract_groups", "item_has_groups", "apply_recipe_filters")
i3.remove_minitab"nodes"
i3.remove_minitab"items"
i3.new_minitab("unlocked", {
description = "Unlocked",
sorter = function(item, data)
return data.items_progress[item]
end
})
local function get_filtered_items(player, data) local function get_filtered_items(player, data)
local items, known = {}, 0 local items, known, c = {}, 0, 0
for i = 1, #i3.init_items do for i = 1, #i3.init_items do
local item = i3.init_items[i] local item = i3.init_items[i]
@ -28,7 +17,8 @@ local function get_filtered_items(player, data)
usages = #apply_recipe_filters(usages or {}, player) usages = #apply_recipe_filters(usages or {}, player)
if recipes > 0 or usages > 0 then if recipes > 0 or usages > 0 then
items[item] = true c++
items[c] = item
known += recipes + usages known += recipes + usages
end end
end end
@ -143,20 +133,13 @@ local function poll_new_items(player, data, join)
if data.discovered > 0 then if data.discovered > 0 then
local msg = fmt("%u new recipe%s unlocked!", data.discovered, data.discovered > 1 and "s" or "") local msg = fmt("%u new recipe%s unlocked!", data.discovered, data.discovered > 1 and "s" or "")
local last_discovered = diff[1] hud_notif(data.player_name, msg, "i3_book.png")
local img = reg_items[last_discovered].inventory_image
if reg_nodes[last_discovered] then
local id = core.get_content_id(last_discovered)
img = i3.cubes[id] or img
end
hud_notif(data.player_name, msg, img)
end end
data.items_progress = items data.items_raw = items
data.itab = 1
sort_by_category(data) search(data)
set_fs(player) set_fs(player)
end end

View File

@ -1,17 +1,14 @@
local fmt = string.format
local PNG = { local PNG = {
blank = "i3_blank.png", blank = "i3_blank.png",
bg = "i3_bg.png", bg = "i3_bg.png",
bg_full = "i3_bg_full.png", bg_full = "i3_bg_full.png",
bg_goto = "i3_bg_goto.png",
bg_content = "i3_bg_content.png", bg_content = "i3_bg_content.png",
bar = "i3_bar.png", bar = "i3_bar.png",
hotbar = "i3_hotbar.png", hotbar = "i3_hotbar.png",
highlight = "i3_highlight.png",
search = "i3_search.png", search = "i3_search.png",
heart = "i3_heart.png", heart = "i3_heart.png",
heart_half = "i3_heart_half.png", heart_half = "i3_heart_half.png",
heart_grey = "i3_heart_grey.png",
prev = "i3_next.png^\\[transformFX", prev = "i3_next.png^\\[transformFX",
next = "i3_next.png", next = "i3_next.png",
arrow = "i3_arrow.png", arrow = "i3_arrow.png",
@ -25,9 +22,8 @@ local PNG = {
book = "i3_book.png", book = "i3_book.png",
sign = "i3_sign.png", sign = "i3_sign.png",
cancel = "i3_cancel.png", cancel = "i3_cancel.png",
crafting = "i3_crafting.png", export = "i3_export.png",
slot = "i3_slot.png^\\[resize:128x128", slot = "i3_slot.png",
pagenum_hover = "i3_slot.png^\\[resize:128x128^\\[opacity:130",
tab = "i3_tab.png", tab = "i3_tab.png",
tab_small = "i3_tab_small.png", tab_small = "i3_tab_small.png",
tab_top = "i3_tab.png^\\[transformFY", tab_top = "i3_tab.png^\\[transformFY",
@ -44,22 +40,14 @@ local PNG = {
nonvisible = "i3_non_visible.png", nonvisible = "i3_non_visible.png",
exit = "i3_exit.png", exit = "i3_exit.png",
home = "i3_home.png", home = "i3_home.png",
flag = "i3_flag_anim.png", flag = "i3_flag.png",
edit = "i3_edit.png", edit = "i3_edit.png",
no_result = "i3_no_result.png", no_result = "i3_no_result.png",
find_more = "i3_find_more.png", find_more = "i3_find_more.png",
search_outline = "i3_search_outline.png",
search_outline_trim = "i3_search_outline_trim.png",
all = "i3_all.png",
node = "i3_node.png",
item = "i3_item.png",
cube = "i3_cube.png",
home_px = "i3_home_px.png",
home_px_hover = "i3_home_px_hover.png",
cancel_hover = "i3_cancel.png^\\[brighten", cancel_hover = "i3_cancel.png^\\[brighten",
search_hover = "i3_search.png^\\[brighten", search_hover = "i3_search.png^\\[brighten",
crafting_hover = "i3_crafting.png^\\[brighten", export_hover = "i3_export.png^\\[brighten",
trash_hover = "i3_trash.png^\\[brighten^\\[colorize:#f00:100", trash_hover = "i3_trash.png^\\[brighten^\\[colorize:#f00:100",
compress_hover = "i3_compress.png^\\[brighten", compress_hover = "i3_compress.png^\\[brighten",
sort_hover = "i3_sort.png^\\[brighten", sort_hover = "i3_sort.png^\\[brighten",
@ -79,27 +67,19 @@ local PNG = {
exit_hover = "i3_exit.png^\\[brighten", exit_hover = "i3_exit.png^\\[brighten",
home_hover = "i3_home.png^\\[brighten", home_hover = "i3_home.png^\\[brighten",
edit_hover = "i3_edit.png^\\[brighten", edit_hover = "i3_edit.png^\\[brighten",
all_hover = "i3_all_on.png^\\[brighten",
node_hover = "i3_node_on.png^\\[brighten",
item_hover = "i3_item_on.png^\\[brighten",
} }
local styles = string.format([[ local styles = string.format([[
listcolors[#bababa50;#bababa99]
style_type[list;size=1;spacing=0.15]
style_type[field;border=false;bgcolor=transparent] style_type[field;border=false;bgcolor=transparent]
style_type[label,field;font_size=16] style_type[label,field;font_size=16]
style_type[button;border=false;content_offset=0] style_type[button;border=false;content_offset=0]
style_type[image_button,item_image_button,checkbox,dropdown;border=false;sound=i3_click] style_type[image_button,item_image_button,checkbox,dropdown;border=false;sound=i3_click]
style_type[item_image_button;bgimg_middle=9;padding=-9] style_type[item_image_button;bgimg_hovered=%s]
style_type[item_image_button:hovered;bgimg=%s]
style[;sound=] style[;sound=]
style[nofav;sound=i3_cannot] style[nofav;sound=i3_cannot]
style[search;content_offset=0]
style[pagenum,no_item,no_rcp;font=bold;font_size=18] style[pagenum,no_item,no_rcp;font=bold;font_size=18]
style[enable_search:hovered;bgimg=%s] style[search;fgimg=%s;content_offset=0]
style[exit;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[exit;fgimg=%s;fgimg_hovered=%s;content_offset=0]
style[cancel;fgimg=%s;fgimg_hovered=%s;content_offset=0] style[cancel;fgimg=%s;fgimg_hovered=%s;content_offset=0]
style[prev_page,prev_recipe,prev_usage,prev_sort,prev_skin;fgimg=%s;fgimg_hovered=%s] style[prev_page,prev_recipe,prev_usage,prev_sort,prev_skin;fgimg=%s;fgimg_hovered=%s]
@ -116,25 +96,24 @@ local styles = string.format([[
style[confirm_trash_yes;sound=i3_trash] style[confirm_trash_yes;sound=i3_trash]
]], ]],
PNG.slot, PNG.slot,
PNG.search_outline, PNG.search_hover,
PNG.exit, PNG.exit_hover, PNG.exit, PNG.exit_hover,
PNG.cancel, PNG.cancel_hover, PNG.cancel, PNG.cancel_hover,
PNG.prev, PNG.prev_hover, PNG.prev, PNG.prev_hover,
PNG.next, PNG.next_hover, PNG.next, PNG.next_hover,
PNG.add, PNG.add_hover, PNG.add, PNG.add_hover,
PNG.edit, PNG.edit_hover) PNG.edit, PNG.edit_hover)
local fs_elements = { local fs_elements = {
label = "label[%f,%f;%s]", label = "label[%f,%f;%s]",
box = "box[%f,%f;%f,%f;%s]", box = "box[%f,%f;%f,%f;%s]",
image = "image[%f,%f;%f,%f;%s]", image = "image[%f,%f;%f,%f;%s]",
tooltip = "tooltip[%f,%f;%f,%f;%s;#32333899;#fff]", tooltip = "tooltip[%f,%f;%f,%f;%s]",
button = "button[%f,%f;%f,%f;%s;%s]", button = "button[%f,%f;%f,%f;%s;%s]",
checkbox = "checkbox[%f,%f;%s;%s;%s]", checkbox = "checkbox[%f,%f;%s;%s;%s]",
slot = "image[%f,%f;%f,%f;" .. fmt("%s;9]", PNG.slot),
item_image = "item_image[%f,%f;%f,%f;%s]", item_image = "item_image[%f,%f;%f,%f;%s]",
hypertext = "hypertext[%f,%f;%f,%f;%s;%s]", hypertext = "hypertext[%f,%f;%f,%f;%s;%s]",
bg9 = "background9[%f,%f;%f,%f;%s;false;12]", bg9 = "background9[%f,%f;%f,%f;%s;false;%u]",
scrollbar = "scrollbar[%f,%f;%f,%f;%s;%s;%u]", scrollbar = "scrollbar[%f,%f;%f,%f;%s;%s;%u]",
model = "model[%f,%f;%f,%f;%s;%s;%s;%s;%s;%s;%s]", model = "model[%f,%f;%f,%f;%s;%s;%s;%s;%s;%s;%s]",
image_button = "image_button[%f,%f;%f,%f;%s;%s;%s]", image_button = "image_button[%f,%f;%f,%f;%s;%s;%s]",

View File

@ -11,39 +11,32 @@ mt3:get_meta():set_string("description", "Worn Pick")
mt3:get_meta():set_string("color", "yellow") mt3:get_meta():set_string("color", "yellow")
mt3:set_wear(10000) mt3:set_wear(10000)
minetest.register_craft { minetest.register_craft({
output = mt:to_string(), output = mt:to_string(),
type = "shapeless", type = "shapeless",
recipe = { recipe = {
"default:wood", "default:wood",
mt2:to_string(), mt2:to_string(),
}, },
} })
minetest.register_craft { minetest.register_craft({
output = mt3:to_string(), output = mt3:to_string(),
type = "shapeless", type = "shapeless",
recipe = { recipe = {
"default:pick_mese", "default:pick_mese",
"default:diamond", "default:diamond",
}, },
} })
minetest.clear_craft {
recipe = {
{"default:sand", "default:sand"},
{"default:sand", "default:sand"},
},
}
i3.register_craft { i3.register_craft {
url = "https://raw.githubusercontent.com/minetest-mods/i3/main/tests/test_online_recipe.json" url = "https://raw.githubusercontent.com/minetest-mods/i3/main/tests/test_online_recipe.json"
} }
i3.register_craft { i3.register_craft({
result = "default:ladder_wood 2", result = "default:ladder_wood 2",
items = {"default:copper_ingot 7, default:tin_ingot, default:steel_ingot 2"}, items = {"default:copper_ingot 7, default:tin_ingot, default:steel_ingot 2"},
} })
i3.register_craft { i3.register_craft {
result = "default:tree", result = "default:tree",
@ -63,7 +56,7 @@ i3.register_craft {
} }
} }
i3.register_craft { i3.register_craft({
grid = { grid = {
"X", "X",
"#", "#",
@ -75,9 +68,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X", "X",
"#X", "#X",
@ -89,9 +82,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X", "X",
}, },
@ -100,10 +93,10 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X#", "X#",
}, },
@ -112,9 +105,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X#X", "X#X",
}, },
@ -123,9 +116,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X#XX", "X#XX",
}, },
@ -134,9 +127,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X#XX", "X#XX",
"X#X", "X#X",
@ -146,9 +139,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X#XX", "X#XX",
"X#X", "X#X",
@ -159,9 +152,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X##XX", "X##XX",
}, },
@ -170,9 +163,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X##X#X", "X##X#X",
}, },
@ -181,9 +174,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X##X#X", "X##X#X",
"", "",
@ -194,9 +187,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -208,9 +201,9 @@ i3.register_craft {
['X'] = "default:glass 2", ['X'] = "default:glass 2",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -222,9 +215,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -237,9 +230,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -252,10 +245,10 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -269,9 +262,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -285,9 +278,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -301,9 +294,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -317,9 +310,9 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })
i3.register_craft { i3.register_craft({
grid = { grid = {
"X #", "X #",
" ## ", " ## ",
@ -335,4 +328,4 @@ i3.register_craft {
['X'] = "default:glass", ['X'] = "default:glass",
}, },
result = "default:mese 3", result = "default:mese 3",
} })

View File

@ -1,32 +1,15 @@
local SWITCH
i3.new_tab("test1", { i3.new_tab("test1", {
description = "Test 1 Test 1", description = "Test 1 Test 1",
image = "i3_heart.png", image = "i3_heart.png",
formspec = function(player, data, fs) formspec = function(player, data, fs)
fs("button", 3, 4, 3, 0.8, "test", "Click here") fs("label[3,1;Test 1]")
fs("label", 3, 1, "Just a test")
if SWITCH then
fs"label[3,2;Button clicked]"
else
fs"label[3,2;Lorem Ipsum]"
end
end, end,
fields = function(player, data, fields)
if fields.test then
SWITCH = true
end
end
}) })
i3.new_tab("test2", { i3.new_tab("test2", {
description = "Test 2", description = "Test 2",
image = "i3_mesepick.png", image = "i3_mesepick.png",
slots = true,
formspec = function(player, data, fs) formspec = function(player, data, fs)
fs("label[3,1;Test 2]") fs("label[3,1;Test 2]")

View File

@ -1,12 +0,0 @@
core.after(5, function()
i3.add_waypoint("Test", {
player = "singleplayer",
pos = {x = 0, y = 2, z = 0},
color = 0xffff00,
-- image = "heart.png",
})
core.after(5, function()
i3.remove_waypoint("singleplayer", "Test")
end)
end)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

BIN
textures/i3_book.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

BIN
textures/i3_export.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
textures/i3_flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

BIN
textures/i3_heart_grey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB