Totally rework how bags work + other cool stuff

This commit is contained in:
Jean-Patrick Guerrero 2021-11-14 21:39:48 +01:00
parent 9cc8464111
commit 2e7dcd714d
15 changed files with 366 additions and 241 deletions

9
API.md
View File

@ -7,8 +7,7 @@
Custom tabs can be added to the `i3` inventory as follow (example):
```Lua
i3.new_tab {
name = "stuff",
i3.new_tab("stuff", {
description = "Stuff",
image = "image.png", -- Optional, adds an image next to the tab description
@ -28,16 +27,16 @@ i3.new_tab {
fields = function(player, data, fields)
end,
}
})
```
- `player` is an `ObjectRef` to the user.
- `data` are the user data.
- `fs` is the formspec table which is callable with a metamethod. Each call adds a new entry.
#### `i3.set_fs(player[, extra_formspec])`
#### `i3.set_fs(player)`
Updates the current formspec. `extra_formspec` adds an additional formspec string.
Updates the current formspec.
#### `i3.remove_tab(tabname)`

View File

@ -10,20 +10,20 @@ This mod features a modern, powerful inventory menu with a good user experience.
This mod requires **Minetest 5.4+**
#### List of features:
- Crafting Guide (only in survival mode)
- Crafting Guide (survival mode only)
- Progressive Mode¹
- Quick Crafting
- Backpacks
- 3D Player Model Preview
- Inventory Sorting (with optional compression)
- 3D Player Model Real-Time Preview
- Inventory Sorting (+ options: compression, reverse mode, automation, etc.)
- Item List Compression (**`moreblocks`** is supported)
- Item Bookmarks
- Waypoints
- Item List Compression (**`moreblocks`** is supported)
- Bags
- Home
**¹** *This mode is a Terraria-like system that shows recipes you can craft from items you ever had in your inventory.
To enable it: `i3_progressive_mode = true` in `minetest.conf`.*
#### This mod officially supports the following mods:
- [**`3d_armor`**](https://content.minetest.net/packages/stu/3d_armor/)
- [**`skinsdb`**](https://content.minetest.net/packages/bell07/skinsdb/)
@ -46,12 +46,10 @@ value of the setting `display_density_factor` in your `minetest.conf`. Note that
`i3` uses a larger inventory than the usual inventories in Minetest games.
Thus, most chests will be unadapted to this inventory size.
The `i3` inventory is 9 slots wide by default (without backpack), such as Minecraft.
The `i3` inventory is 9 slots wide by default, such as Minecraft.
Report any bug on the [**Bug Tracker**](https://github.com/minetest-mods/i3/issues).
Report bugs on the [**Bug Tracker**](https://github.com/minetest-mods/i3/issues).
Love this mod? Donations are appreciated: https://www.paypal.me/jpg84240
Demo video (outdated): https://www.youtube.com/watch?v=25nCAaqeacU
![Preview](https://user-images.githubusercontent.com/7883281/140816791-693a5c8a-a7d1-47d4-a45d-883cc008ae8a.png)

View File

@ -15,13 +15,6 @@ i3 = {
MIN_FORMSPEC_VERSION = 4,
SAVE_INTERVAL = 600, -- Player data save interval (in seconds)
BAG_SIZES = {
4*9 + 3,
4*9 + 6,
4*9 + 9,
4*9 + 25,
},
SUBCAT = {
"bag",
"armor",
@ -36,7 +29,7 @@ i3 = {
bag_size = true,
waypoints = true,
inv_items = true,
reject_items = true,
drop_items = true,
known_recipes = true,
},
@ -56,18 +49,19 @@ i3 = {
files = {
api = lf("/src/api.lua"),
bags = lf("/src/bags.lua"),
callbacks = lf("/src/callbacks.lua"),
common = lf("/src/common.lua"),
compress = lf("/src/compress.lua"),
detached = lf("/src/detached_inv.lua"),
groups = lf("/src/groups.lua"),
gui = lf("/src/gui.lua"),
inventory = lf("/src/inventory.lua"),
model_alias = lf("/src/model_aliases.lua"),
progressive = lf("/src/progressive.lua"),
recipes = lf("/src/recipes.lua"),
styles = lf("/src/styles.lua"),
},
progressive_mode = core.settings:get_bool "i3_progressive_mode",
progressive_mode = core.settings:get_bool"i3_progressive_mode",
item_compression = core.settings:get_bool("i3_item_compression", true),
}
@ -75,14 +69,16 @@ i3.files.common()
i3.files.api()
i3.files.compress()
i3.files.groups()
i3.files.inventory()
i3.files.callbacks()
local storage = core.get_mod_storage()
local slz, dslz, str_to_pos, add_hud_waypoint = i3.get("slz", "dslz", "str_to_pos", "add_hud_waypoint")
local slz, dslz, ESC, str_to_pos, add_hud_waypoint =
i3.get("slz", "dslz", "ESC", "str_to_pos", "add_hud_waypoint")
i3.data = dslz(storage:get_string "data") or {}
local init_backpack = i3.files.bags()
local init_bags = i3.files.bags()
local init_inventories = i3.files.detached()
local init_recipes = i3.files.recipes()
local function get_lang_code(info)
@ -146,6 +142,7 @@ local function init_data(player, info)
i3.data[name] = i3.data[name] or {}
local data = i3.data[name]
data.player_name = ESC(name)
data.filter = ""
data.pagenum = 1
data.items = i3.init_items
@ -165,6 +162,9 @@ local function init_data(player, info)
data.lang_code = get_lang_code(info)
data.fs_version = info.formspec_version
local inv = player:get_inventory()
inv:set_size("main", i3.INV_SIZE)
core.after(0, i3.set_fs, player)
end
@ -216,12 +216,12 @@ core.register_on_joinplayer(function(player)
local info = core.get_player_information and core.get_player_information(name)
if not info or get_formspec_version(info) < i3.MIN_FORMSPEC_VERSION then
i3.data[name] = nil
return outdated(name)
end
init_data(player, info)
init_backpack(player)
init_bags(player)
init_inventories(player)
init_waypoints(player)
init_hudbar(player)
end)

BIN
sounds/i3_cannot.ogg Normal file

Binary file not shown.

View File

@ -161,7 +161,7 @@ function i3.get_recipes(item)
}
end
function i3.set_fs(player, _fs)
function i3.set_fs(player)
if not player or player.is_fake_player then return end
local name = player:get_player_name()
local data = i3.data[name]
@ -171,32 +171,34 @@ function i3.set_fs(player, _fs)
sort_inventory(player, data)
end
local fs = fmt("%s%s", make_fs(player, data), _fs or "")
local fs = make_fs(player, data)
player:set_inventory_formspec(fs)
end
function i3.new_tab(def)
if not true_table(def) then
return err "i3.new_tab: tab definition missing"
elseif not true_str(def.name) then
function i3.new_tab(name, def)
if not true_str(name) then
return err "i3.new_tab: tab name missing"
elseif not true_table(def) then
return err "i3.new_tab: tab definition missing"
elseif not true_str(def.description) then
return err "i3.new_tab: description missing"
elseif #i3.tabs == 6 then
return err(fmt("i3.new_tab: cannot add '%s' tab. Limit reached (6).", def.name))
end
i3.tabs[#i3.tabs + 1] = def
def.name = name
insert(i3.tabs, def)
end
function i3.remove_tab(tabname)
if not true_str(tabname) then
function i3.remove_tab(name)
if not true_str(name) then
return err "i3.remove_tab: tab name missing"
end
for i, def in ipairs(i3.tabs) do
if tabname == def.name then
if name == def.name then
remove(i3.tabs, i)
break
end
end
end
@ -217,32 +219,31 @@ function i3.set_tab(player, tabname)
return
end
local found
for i, def in ipairs(i3.tabs) do
if not found and def.name == tabname then
if def.name == tabname then
data.tab = i
found = true
return
end
end
if not found then
return err(fmt("i3.set_tab: tab name '%s' does not exist", tabname))
end
err(fmt("i3.set_tab: tab name '%s' does not exist", tabname))
end
function i3.override_tab(tabname, newdef)
if not true_table(newdef) then
return err "i3.override_tab: tab definition missing"
elseif not true_str(newdef.name) then
function i3.override_tab(name, newdef)
if not true_str(name) then
return err "i3.override_tab: tab name missing"
elseif not true_table(newdef) then
return err "i3.override_tab: tab definition missing"
elseif not true_str(newdef.description) then
return err "i3.override_tab: description missing"
end
newdef.name = name
for i, def in ipairs(i3.tabs) do
if def.name == tabname then
if def.name == name then
i3.tabs[i] = newdef
break
end
end
end

View File

@ -1,55 +1,161 @@
local S, fmt, msg, spawn_item = i3.get("S", "fmt", "msg", "spawn_item")
local S, ES, fmt, clr, msg, slz, dslz, play_sound, create_inventory =
i3.get("S", "ES", "fmt", "clr", "msg", "slz", "dslz", "play_sound", "create_inventory")
local function init_backpack(player)
local function get_content_inv(name)
return core.get_inventory {
type = "detached",
name = fmt("i3_bag_content_%s", name)
}
end
local function get_content(content)
local t = {}
for i, v in pairs(content) do
local stack = ItemStack(v.name)
local meta, wear = v.meta, v.wear
if meta then
local m = stack:get_meta()
m:from_table(meta)
end
if wear then
stack:set_wear(wear)
end
t[i] = stack
end
return t
end
local function safe_format(stack)
local meta = stack:get_meta():to_table()
local wear = stack:get_wear()
local has_meta = next(meta.fields)
local info = {}
info.name = fmt("%s %u", stack:get_name(), stack:get_count())
if has_meta then
info.meta = meta
end
if wear > 0 then
info.wear = wear
end
return info
end
local function init_bags(player)
local name = player:get_player_name()
local data = i3.data[name]
local inv = player:get_inventory()
inv:set_size("main", data.bag_size and i3.BAG_SIZES[data.bag_size] or i3.INV_SIZE)
local bag = create_inventory(fmt("i3_bag_%s", name), {
allow_put = function(inv, _, _, stack)
local empty = inv:is_empty"main"
local item_group = core.get_item_group(stack:get_name(), "bag")
data.bag = core.create_detached_inventory(fmt("%s_backpack", name), {
allow_put = function(_inv, listname, _, stack)
local empty = _inv:get_stack(listname, 1):is_empty()
local item_group = minetest.get_item_group(stack:get_name(), "bag")
if empty and item_group > 0 and item_group <= #i3.BAG_SIZES then
if empty and item_group > 0 and item_group <= 4 then
return 1
end
msg(name, S"This is not a backpack")
return 0
if not empty then
msg(name, S"There is already a bag")
else
msg(name, S"This is not a bag")
end
return 0, play_sound(name, "i3_cannot", 0.8)
end,
on_put = function(_, _, _, stack)
local stackname = stack:get_name()
data.bag_item = stackname
data.bag_size = minetest.get_item_group(stackname, "bag")
data.bag_item = safe_format(stack)
data.bag_size = core.get_item_group(stack:get_name(), "bag")
local meta = stack:get_meta()
local content = dslz(meta:get_string"content")
if content then
local inv = get_content_inv(name)
inv:set_list("main", get_content(content))
end
inv:set_size("main", i3.BAG_SIZES[data.bag_size])
i3.set_fs(player)
end,
on_take = function()
for i = i3.INV_SIZE + 1, i3.BAG_SIZES[data.bag_size] do
local stack = inv:get_stack("main", i)
if not stack:is_empty() then
spawn_item(player, stack)
end
end
data.bag_item = nil
data.bag_size = nil
inv:set_size("main", i3.INV_SIZE)
local content = get_content_inv(name)
content:set_list("main", {})
i3.set_fs(player)
end,
})
}, name)
data.bag:set_size("main", 1)
bag:set_size("main", 1)
if data.bag_item then
data.bag:set_stack("main", 1, data.bag_item)
bag:set_list("main", get_content{data.bag_item})
end
local function save_content(inv)
local bagstack = bag:get_stack("main", 1)
local meta = bagstack:get_meta()
if inv:is_empty("main") then
meta:set_string("description", "")
meta:set_string("content", "")
else
local list = inv:get_list"main"
local t = {}
for i = 1, #list do
local stack = list[i]
if not stack:is_empty() then
t[i] = safe_format(stack)
end
end
local function count_items()
local c = 0
for _ in pairs(t) do
c = c + 1
end
return c
end
meta:set_string("description", "")
meta:set_string("description", ES("@1 (contains @2 / @3 stacks)",
bagstack:get_short_description(), clr("#ff0", count_items()), data.bag_size * 4))
meta:set_string("content", slz(t))
end
bag:set_stack("main", 1, bagstack)
data.bag_item = safe_format(bagstack)
i3.set_fs(player)
end
local bag_content = create_inventory(fmt("i3_bag_content_%s", name), {
on_move = save_content,
on_put = save_content,
on_take = save_content,
}, name)
bag_content:set_size("main", 4*4)
if data.bag_item then
local meta = bag:get_stack("main", 1):get_meta()
local content = dslz(meta:get_string"content") or {}
bag_content:set_list("main", get_content(content))
end
end
@ -60,21 +166,21 @@ local bag_recipes = {
{"group:wool", "group:wool", "group:wool"},
{"group:wool", "group:wool", "group:wool"},
},
size = 1,
size = 2,
},
medium = {
rcp = {
{"farming:string", "i3:bag_small", "farming:string"},
{"farming:string", "i3:bag_small", "farming:string"},
},
size = 2,
size = 3,
},
large = {
rcp = {
{"farming:string", "i3:bag_medium", "farming:string"},
{"farming:string", "i3:bag_medium", "farming:string"},
},
size = 3,
size = 4,
},
}
@ -84,12 +190,12 @@ for size, item in pairs(bag_recipes) do
core.register_craftitem(bagname, {
description = fmt("%s Backpack", size:gsub("^%l", string.upper)),
inventory_image = fmt("i3_bag_%s.png", size),
groups = {bag = item.size},
stack_max = 1,
groups = {bag = item.size}
})
core.register_craft {output = bagname, recipe = item.rcp}
core.register_craft {type = "fuel", recipe = bagname, burntime = 3}
core.register_craft{output = bagname, recipe = item.rcp}
core.register_craft{type = "fuel", recipe = bagname, burntime = 3}
end
return init_backpack
return init_bags

View File

@ -1,20 +1,18 @@
local _, get_inventory_fs = i3.files.gui()
local S, clr = i3.get("S", "clr")
local min, ceil, random = i3.get("min", "ceil", "random")
local min, random = i3.get("min", "random")
local reg_items, reg_aliases = i3.get("reg_items", "reg_aliases")
local fmt, find, match, sub, lower, split = i3.get("fmt", "find", "match", "sub", "lower", "split")
local vec_new, vec_eq, vec_round = i3.get("vec_new", "vec_eq", "vec_round")
local sort, copy, insert, remove, indexof = i3.get("sort", "copy", "insert", "remove", "indexof")
local is_group, extract_groups, groups_to_items =
i3.get("is_group", "extract_groups", "groups_to_items")
local msg, is_fav, pos_to_str, str_to_pos, add_hud_waypoint =
i3.get("msg", "is_fav", "pos_to_str", "str_to_pos", "add_hud_waypoint")
local msg, is_fav, pos_to_str, str_to_pos, add_hud_waypoint, play_sound =
i3.get("msg", "is_fav", "pos_to_str", "str_to_pos", "add_hud_waypoint", "play_sound")
local search, get_sorting_idx, sort_inventory, sort_by_category, get_recipes =
i3.get("search", "get_sorting_idx", "sort_inventory", "sort_by_category", "get_recipes")
local show_item, get_stack, clean_name, compressible, check_privs, safe_teleport =
i3.get("show_item", "get_stack", "clean_name", "compressible", "check_privs", "safe_teleport")
local show_item, get_stack, craft_stack, clean_name, compressible, check_privs, safe_teleport =
i3.get("show_item", "get_stack", "craft_stack", "clean_name", "compressible", "check_privs", "safe_teleport")
local function reset_data(data)
data.filter = ""
@ -40,13 +38,12 @@ local function reset_data(data)
end
end
i3.new_tab {
name = "inventory",
i3.new_tab("inventory", {
description = S"Inventory",
formspec = get_inventory_fs,
fields = function(player, data, fields)
local name = player:get_player_name()
local name = data.player_name
local inv = player:get_inventory()
local sb_inv = fields.scrbar_inv
@ -56,9 +53,9 @@ i3.new_tab {
skins.set_player_skin(player, _skins[id])
end
if fields.reject_items then
local items = split(fields.reject_items, ",")
data.reject_items = items
if fields.drop_items then
local items = split(fields.drop_items, ",")
data.drop_items = items
end
for field in pairs(fields) do
@ -176,6 +173,7 @@ i3.new_tab {
for _, v in ipairs(data.waypoints) do
if vec_eq(vec_round(pos), vec_round(str_to_pos(v.pos))) then
play_sound(name, "i3_cannot", 0.8)
return msg(name, "You already set a waypoint at this position")
end
end
@ -201,9 +199,9 @@ i3.new_tab {
return i3.set_fs(player)
end,
}
})
local function select_item(player, name, data, _f)
local function select_item(player, data, _f)
local item
for field in pairs(_f) do
@ -271,7 +269,7 @@ local function select_item(player, name, data, _f)
item = reg_aliases[item] or item
if not reg_items[item] then return end
if core.is_creative_enabled(name) then
if core.is_creative_enabled(data.player_name) then
local stack = ItemStack(item)
local stackmax = stack:get_stack_max()
stack = fmt("%s %s", item, stackmax)
@ -294,55 +292,7 @@ local function select_item(player, name, data, _f)
end
end
local function craft_stack(player, data, craft_rcp)
local inv = player:get_inventory()
local rcp_usg = craft_rcp and "recipe" or "usage"
local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output
output = ItemStack(output)
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
for name, count in pairs(data.export_counts[rcp_usg].rcp) do
local items = {[name] = count}
if is_group(name) then
items = {}
local groups = extract_groups(name)
local item_groups = groups_to_items(groups, true)
local remaining = count
for _, item in ipairs(item_groups) do
for _name, _count in pairs(data.export_counts[rcp_usg].inv) do
if item == _name and remaining > 0 then
local c = min(remaining, _count)
items[item] = c
remaining = remaining - c
end
if remaining == 0 then break end
end
end
end
for k, v in pairs(items) do
inv:remove_item("main", fmt("%s %s", k, v * scrbar_val))
end
end
local count = stackcount * scrbar_val
local iter = ceil(count / stackmax)
local leftover = count
for _ = 1, iter do
local c = min(stackmax, leftover)
local stack = ItemStack(fmt("%s %s", stackname, c))
get_stack(player, stack)
leftover = leftover - stackmax
end
end
local function rcp_fields(player, data, fields)
local name = player:get_player_name()
local sb_rcp, sb_usg = fields.scrbar_rcp, fields.scrbar_usg
if fields.cancel then
@ -431,7 +381,7 @@ local function rcp_fields(player, data, fields)
data.scrbar_usg = 1
end
else
select_item(player, name, data, fields)
select_item(player, data, fields)
end
end
@ -439,11 +389,12 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if formname == "i3_outdated" then
return false, core.kick_player(name, "Come back when your client is up-to-date.")
return false, core.kick_player(name, S"Come back when your client is up-to-date.")
elseif formname ~= "" then
return false
end
--print(dump(fields))
local data = i3.data[name]
if not data then return end
@ -456,6 +407,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
data.pagenum = 1
data.itab = tonumber(f:sub(-1))
sort_by_category(data)
break
end
end
@ -486,15 +438,6 @@ core.register_on_dieplayer(function(player)
local data = i3.data[name]
if not data then return end
if data.bag_size then
data.bag_item = nil
data.bag_size = nil
data.bag:set_list("main", {})
local inv = player:get_inventory()
inv:set_size("main", i3.INV_SIZE)
end
i3.set_fs(player)
end)
@ -524,27 +467,3 @@ core.register_on_player_inventory_action(function(player, _, _, info)
i3.set_fs(player)
end
end)
local trash = core.create_detached_inventory("i3_trash", {
allow_put = function(_, _, _, stack)
return stack:get_count()
end,
on_put = function(inv, listname, _, _, player)
inv:set_list(listname, {})
local name = player:get_player_name()
core.sound_play("i3_trash", {to_player = name, gain = 1.0}, true)
if not core.is_creative_enabled(name) then
i3.set_fs(player)
end
end,
})
trash:set_size("main", 1)
local output_rcp = core.create_detached_inventory("i3_output_rcp", {})
output_rcp:set_size("main", 1)
local output_usg = core.create_detached_inventory("i3_output_usg", {})
output_usg:set_size("main", 1)

View File

@ -349,9 +349,60 @@ local function get_stack(player, stack)
end
end
local function craft_stack(player, data, craft_rcp)
local inv = player:get_inventory()
local rcp_usg = craft_rcp and "recipe" or "usage"
local output = craft_rcp and data.recipes[data.rnum].output or data.usages[data.unum].output
output = ItemStack(output)
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
for name, count in pairs(data.export_counts[rcp_usg].rcp) do
local items = {[name] = count}
if is_group(name) then
items = {}
local groups = extract_groups(name)
local item_groups = groups_to_items(groups, true)
local remaining = count
for _, item in ipairs(item_groups) do
for _name, _count in pairs(data.export_counts[rcp_usg].inv) do
if item == _name and remaining > 0 then
local c = math.min(remaining, _count)
items[item] = c
remaining = remaining - c
end
if remaining == 0 then break end
end
end
end
for k, v in pairs(items) do
inv:remove_item("main", fmt("%s %s", k, v * scrbar_val))
end
end
local count = stackcount * scrbar_val
local iter = math.ceil(count / stackmax)
local leftover = count
for _ = 1, iter do
local c = math.min(stackmax, leftover)
local stack = ItemStack(fmt("%s %s", stackname, c))
get_stack(player, stack)
leftover = leftover - stackmax
end
end
local function play_sound(name, sound, volume)
core.sound_play(sound, {to_player = name, gain = volume}, true)
end
local function safe_teleport(player, pos)
local name = player:get_player_name()
core.sound_play("i3_teleport", {to_player = name, gain = 1.0}, true)
play_sound(name, "i3_teleport", 1.0)
pos.y = pos.y + 0.5
local vel = player:get_velocity()
@ -446,7 +497,7 @@ local function compress_items(list, start_i)
return new_inv
end
local function reject_items(player, inv, list, start_i, rej)
local function drop_items(player, inv, list, start_i, rej)
for i = start_i, #list do
local stack = list[i]
local name = stack:get_name()
@ -459,17 +510,17 @@ local function reject_items(player, inv, list, start_i, rej)
end
end
return inv:get_list("main")
return inv:get_list"main"
end
local function sort_inventory(player, data)
local inv = player:get_inventory()
local list = inv:get_list("main")
local size = inv:get_size("main")
local list = inv:get_list"main"
local size = inv:get_size"main"
local start_i = data.ignore_hotbar and 10 or 1
if true_table(data.reject_items) then
list = reject_items(player, inv, list, start_i, data.reject_items)
if true_table(data.drop_items) then
list = drop_items(player, inv, list, start_i, data.drop_items)
end
if data.inv_compress then
@ -540,9 +591,11 @@ local _ = {
-- Misc. functions
get_stack = get_stack,
craft_stack = craft_stack,
show_item = show_item,
spawn_item = spawn_item,
clean_name = clean_name,
play_sound = play_sound,
safe_teleport = safe_teleport,
add_hud_waypoint = add_hud_waypoint,
@ -554,6 +607,7 @@ local _ = {
pos_to_str = core.pos_to_string,
str_to_pos = core.string_to_pos,
check_privs = core.check_player_privs,
create_inventory = core.create_detached_inventory,
-- Registered items
reg_items = core.registered_items,

32
src/detached_inv.lua Normal file
View File

@ -0,0 +1,32 @@
local fmt, play_sound, create_inventory = i3.get("fmt", "play_sound", "create_inventory")
local trash = create_inventory("i3_trash", {
allow_put = function(_, _, _, stack)
return stack:get_count()
end,
on_put = function(inv, listname, _, _, player)
inv:set_list(listname, {})
local name = player:get_player_name()
play_sound(name, "i3_trash", 1.0)
if not core.is_creative_enabled(name) then
i3.set_fs(player)
end
end,
})
trash:set_size("main", 1)
local function init_inventories(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_inventories

View File

@ -124,10 +124,8 @@ local function get_stack_max(inv, data, is_recipe, rcp)
return max_stacks
end
local function get_inv_slots(data, fs)
local inv_x, inv_y = 0.22, 6.9
local width, size, spacing = i3.HOTBAR_LEN, 1, 0.1
local bag = data.bag_size
local function get_inv_slots(fs)
local inv_x, inv_y, size, spacing = 0.22, 6.9, 1, 0.1
fs("style_type[box;colors=#77777710,#77777710,#777,#777]")
@ -138,14 +136,9 @@ local function get_inv_slots(data, fs)
fs(fmt("style_type[list;size=%f;spacing=%f]", size, spacing),
fmt("list[current_player;main;%f,%f;%u,1;]", inv_x, inv_y, i3.HOTBAR_LEN))
if bag then
local params = {{10, 0.892}, {11, 0.8}, {12, 0.726}, {13, 0.663}}
width, size = unpack(params[bag])
end
fs(fmt("style_type[list;size=%f;spacing=%f]", size, spacing),
fmt("list[current_player;main;%f,%f;%u,%u;%u]", inv_x, inv_y + 1.15,
width, (bag and i3.BAG_SIZES[data.bag_size] or i3.INV_SIZE) / width, i3.HOTBAR_LEN),
i3.HOTBAR_LEN, i3.INV_SIZE / i3.HOTBAR_LEN, i3.HOTBAR_LEN),
"style_type[list;size=1;spacing=0.15]")
fs("listring[current_player;craft]listring[current_player;main]")
@ -294,8 +287,8 @@ local function get_waypoint_fs(fs, data, player, yextra, ctn_len)
end
local function get_container(fs, data, player, yoffset, ctn_len, award_list, awards_unlocked, award_list_nb)
local name = player:get_player_name()
add_subtitle(fs, "player_name", 0, ctn_len, 22, true, ESC(name))
local name = data.player_name
add_subtitle(fs, "player_name", 0, ctn_len, 22, true, name)
if damage_enabled then
local hp = data.hp or player:get_hp() or 20
@ -342,20 +335,39 @@ local function get_container(fs, data, player, yoffset, ctn_len, award_list, awa
local function not_installed(modname)
fs("hypertext", 0, yextra + 0.9, ctn_len, 0.6, "not_installed",
fmt("<center><style color=#7bf font=mono>%s</style> not installed</center>", modname))
fmt("<global size=16><center><style color=#7bf font=mono>%s</style> not installed</center>",
modname))
end
if data.subcat == 1 then
fs(fmt("list[detached:%s_backpack;main;0,%f;1,1;]", ESC(name), yextra + 0.7))
fs(fmt("list[detached:i3_bag_%s;main;0,%f;1,1;]", name, yextra + 0.7))
if not data.bag:get_stack("main", 1):is_empty() then
fs("hypertext", 1.2, yextra + 0.89, ctn_len - 1.9, 0.8, "bpk",
ES("The inventory is extended by @1 slots", i3.BAG_SIZES[data.bag_size] - i3.INV_SIZE))
local inv = core.get_inventory {
type = "detached",
name = fmt("i3_bag_%s", data.player_name)
}
if not inv:is_empty"main" then
fs("image", 0.5, yextra + 1.85, 0.6, 0.6, PNG.arrow_content)
fs(fmt("style[content;bgimg=%s;fgimg=i3_blank.png;bgimg_middle=10,10;sound=]", PNG.bg_content))
fs("image_button", 1.1, yextra + 0.5, 4.75, 4.75, "", "content", "")
fs("hypertext", 1.3, yextra + 0.8, 4.3, 0.6, "content",
fmt("<global size=16><center><b>%s</b></center>", ES"Content"))
local x, size, spacing = 1.45, 0.9, 0.12
if data.bag_size == 4 then
x, size, spacing = 1.7, 0.8, 0.1
end
fs(fmt("style_type[list;size=%f;spacing=%f]", size, spacing))
fs(fmt("list[detached:i3_bag_content_%s;main;%f,%f;4,%u;]", name, x, yextra + 1.3, data.bag_size))
fs("style_type[list;size=1;spacing=0.15]")
end
elseif data.subcat == 2 then
if i3.modules.armor then
fs(fmt("list[detached:%s_armor;armor;0,%f;3,2;]", ESC(name), yextra + 0.7))
fs(fmt("list[detached:%s_armor;armor;0,%f;3,2;]", name, yextra + 0.7))
local armor_def = armor.def[name]
@ -438,13 +450,18 @@ local function show_popup(fs, data)
fs("button", 5.8, 9.25, 1.8, 0.55, "setting_misc", "Misc.")
if show_home then
local home_pos = data.home or ""
home_pos = home_pos:gsub(",", ", "):sub(2,-2):gsub("%.%d", ""):gsub(
"(%-?%d+)", clr("#dbeeff", "%1"))
local home_str = fmt("Home position: %s", home_pos)
home_str = data.home and home_str or ES"No home set"
local coords, c, str = {"X", "Y", "Z"}, 0, ES"No home set"
fs("button", 2.1, 9.7, 6, 0.8, "", home_str)
if data.home then
str = data.home:gsub(",", " "):sub(2,-2):gsub("%.%d", ""):gsub(
"(%-?%d+)", function(a)
c = c + 1
return fmt("<b>%s:</b> <style color=#dbeeff font=mono>%s</style>",
coords[c], a)
end)
end
fs("hypertext", 2.1, 9.9, 6, 0.6, "home_pos", fmt("<global size=16><center>%s</center>", str))
fs("image_button", 4.2, 10.4, 1.8, 0.7, "", "set_home", "Set home")
elseif show_sorting then
@ -476,11 +493,11 @@ local function show_popup(fs, data)
fs("box", 5.4, 10.68, 2.4, 0.45, "#707070")
end
fs("style[reject_items;font_size=15;font=mono;textcolor=#dbeeff]")
fs(fmt("field[5.4,10.68;2.4,0.45;reject_items;Reject items:;%s]",
ESC(concat(data.reject_items or {}, ","))))
fs("field_close_on_enter[reject_items;false]")
fs(fmt("tooltip[reject_items;%s;#707070;#fff]",
fs("style[drop_items;font_size=15;font=mono;textcolor=#dbeeff]")
fs(fmt("field[5.4,10.68;2.4,0.45;drop_items;Drop items:;%s]",
ESC(concat(data.drop_items or {}, ","))))
fs("field_close_on_enter[drop_items;false]")
fs(fmt("tooltip[drop_items;%s;#707070;#fff]",
ES"Format:" .. "\n" ..
("mod:item,mod:item, ..."):gsub("(%a+:%a+)", clr("#bddeff", "%1"))))
end
@ -490,11 +507,9 @@ end
local function get_inventory_fs(player, data, fs)
fs("listcolors[#bababa50;#bababa99]")
get_inv_slots(data, fs)
get_inv_slots(fs)
local props = player:get_properties()
local name = player:get_player_name()
local ctn_len, ctn_hgt = 5.7, 6.3
local yoffset = 0
@ -522,7 +537,10 @@ local function get_inventory_fs(player, data, fs)
local awards_unlocked = 0
local max_val = damage_enabled and 12 or 7
if i3.modules.armor and data.subcat == 2 then
if data.subcat == 1 and data.bag_size then
max_val = max_val + 32
elseif i3.modules.armor and data.subcat == 2 then
if data.scrbar_inv >= max_val then
data.scrbar_inv = data.scrbar_inv + 10
end
@ -530,7 +548,7 @@ local function get_inventory_fs(player, data, fs)
max_val = max_val + 10
elseif i3.modules.awards and data.subcat == 4 then
award_list = awards.get_award_states(name)
award_list = awards.get_award_states(data.player_name)
award_list_nb = #award_list
for i = 1, award_list_nb do
@ -710,19 +728,18 @@ local function get_output_fs(fs, data, rcp, is_recipe, shapeless, right, btn_siz
fs(fmt("style_type[list;size=%f]", i3.ITEM_BTN_SIZE))
fs("listcolors[#bababa50;#bababa99]")
fs(fmt("list[detached:i3_output_%s;main;%f,%f;1,1;]", rcp_usg, X + 0.11, Y))
fs(fmt("list[detached:i3_output_%s_%s;main;%f,%f;1,1;]", rcp_usg, data.player_name, X + 0.11, Y))
fs("button", X + 0.11, Y, i3.ITEM_BTN_SIZE, i3.ITEM_BTN_SIZE, _name, "")
local inv = core.get_inventory {
type = "detached",
name = fmt("i3_output_%s", rcp_usg)
name = fmt("i3_output_%s_%s", rcp_usg, data.player_name)
}
inv:set_stack("main", 1, item)
pos = {x = X + 0.11, y = Y}
else
fs("image", X, Y - 0.11, bt_s, bt_s, PNG.slot)
fs("item_image_button",
X + 0.11, Y, i3.ITEM_BTN_SIZE, i3.ITEM_BTN_SIZE,
fmt("%s %u", name, count * (is_recipe and data.scrbar_rcp or data.scrbar_usg or 1)),

View File

@ -1,6 +1,7 @@
local PNG = {
bg = "i3_bg.png",
bg_full = "i3_bg_full.png",
bg_content = "i3_bg_content.png",
bar = "i3_bar.png",
hotbar = "i3_hotbar.png",
search = "i3_search.png",
@ -10,6 +11,7 @@ local PNG = {
prev = "i3_next.png^\\[transformFX",
next = "i3_next.png",
arrow = "i3_arrow.png",
arrow_content = "i3_arrow_content.png",
trash = "i3_trash.png",
sort = "i3_sort.png",
settings = "i3_settings.png",
@ -66,9 +68,10 @@ local styles = string.format([[
style_type[field;border=false;bgcolor=transparent]
style_type[label,field;font_size=16]
style_type[button;border=false;content_offset=0]
style_type[image_button,item_image_button,checkbox;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_hovered=%s]
style[nofav;sound=i3_cannot]
style[pagenum,no_item,no_rcp;font=bold;font_size=18]
style[exit;fgimg=%s;fgimg_hovered=%s;content_offset=0]
style[cancel;fgimg=%s;fgimg_hovered=%s;content_offset=0]

View File

@ -1,25 +1,22 @@
i3.new_tab {
name = "test1",
i3.new_tab("test1", {
description = "Test 1 Test 1",
image = "i3_heart.png",
formspec = function(player, data, fs)
fs("label[3,1;Test 1]")
end,
}
})
i3.new_tab {
name = "test2",
i3.new_tab("test2", {
description = "Test 2",
image = "i3_mesepick.png",
formspec = function(player, data, fs)
fs("label[3,1;Test 2]")
end,
}
})
i3.new_tab {
name = "test3",
i3.new_tab("test3", {
description = "Test 3",
access = function(player, data)
@ -36,10 +33,9 @@ i3.new_tab {
fields = function(player, data, fields)
i3.set_fs(player, "label[3,2;Test extra_fs]")
end,
}
})
i3.override_tab("test2", {
name = "test2",
description = "Test override",
image = "i3_mesepick.png",

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
textures/i3_bg_content.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
textures/i3_blank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B