["\x70"] = "\x7b", --
["\x50"] = "\x7c", --
["\x71"] = "\x7d", --
["\x51"] = "\x7e", --
["\x72"] = "\x7f", --
["\x52"] = "\x80", --
["\x73"] = "\x85", --
["\x53"] = "\x86", --
["\x74"] = "\x89", --
["\x54"] = "\x8a", --
["\x75"] = "\x8d", --
["\x55"] = "\x8e", --
["\x76"] = "\x93", --
["\x56"] = "\x94", --
["\x77"] = "\x95", --
["\x57"] = "\x96", --
["\x78"] = "\x97", --
["\x58"] = "\x98", --
["\x79"] = "\x99", --
["\x59"] = "\x9a", --
["\x7a"] = "\x9d", --
["\x5a"] = "\x9e", --
["\x30"] = "\xa1", -- <0>
["\x31"] = "\xa2", -- <1>
["\x32"] = "\xa3", -- <2>
["\x33"] = "\xa4", -- <3>
["\x34"] = "\xa5", -- <4>
["\x35"] = "\xa6", -- <5>
["\x36"] = "\xa7", -- <6>
["\x37"] = "\xa8", -- <7>
["\x38"] = "\xa9", -- <8>
["\x39"] = "\xaa", -- <9>
}
local utf8_sort_data_2 = {
["\xc3\xa1"] = "\x43", -- <á>
["\xc3\x81"] = "\x44", -- <Á>
["\xc3\xa4"] = "\x45", -- <ä>
["\xc3\x84"] = "\x46", -- <Ä>
["\xc4\x8d"] = "\x4b", -- <č>
["\xc4\x8c"] = "\x4c", -- <Č>
["\xc4\x8f"] = "\x4f", -- <ď>
["\xc4\x8e"] = "\x50", -- <Ď>
["\xc3\xa9"] = "\x53", -- <é>
["\xc3\x89"] = "\x54", -- <É>
["\xc4\x9b"] = "\x55", -- <ě>
["\xc4\x9a"] = "\x56", -- <Ě>
["\x63\x68"] = "\x5d", --
["\x63\x48"] = "\x5e", --
["\x43\x68"] = "\x5f", --
["\x43\x48"] = "\x60", --
["\xc3\xad"] = "\x63", -- <í>
["\xc3\x8d"] = "\x64", -- <Í>
["\xc4\xba"] = "\x6b", -- <ĺ>
["\xc4\xb9"] = "\x6c", -- <Ĺ>
["\xc4\xbe"] = "\x6d", -- <ľ>
["\xc4\xbd"] = "\x6e", -- <Ľ>
["\xc5\x88"] = "\x73", -- <ň>
["\xc5\x87"] = "\x74", -- <Ň>
["\xc3\xb3"] = "\x77", -- <ó>
["\xc3\x93"] = "\x78", -- <Ó>
["\xc3\xb4"] = "\x79", -- <ô>
["\xc3\x94"] = "\x7a", -- <Ô>
["\xc5\x95"] = "\x81", -- <ŕ>
["\xc5\x94"] = "\x82", -- <Ŕ>
["\xc5\x99"] = "\x83", -- <ř>
["\xc5\x98"] = "\x84", -- <Ř>
["\xc5\xa1"] = "\x87", -- <š>
["\xc5\xa0"] = "\x88", -- <Š>
["\xc5\xa5"] = "\x8b", -- <ť>
["\xc5\xa4"] = "\x8c", -- <Ť>
["\xc3\xba"] = "\x8f", -- <ú>
["\xc3\x9a"] = "\x90", -- <Ú>
["\xc5\xaf"] = "\x91", -- <ů>
["\xc5\xae"] = "\x92", -- <Ů>
["\xc3\xbd"] = "\x9b", -- <ý>
["\xc3\x9d"] = "\x9c", -- <Ý>
["\xc5\xbe"] = "\x9f", -- <ž>
["\xc5\xbd"] = "\xa0", -- <Ž>
}
local utf8_sort_data_3 = {
["\x63"] = "\x49", --
["\x43"] = "\x4a", --
}
local entity_properties_list = {
"hp_max",
"breath_max",
"zoom_fov",
"eye_height",
"physical",
"collide_with_objects",
"collisionbox",
"selectionbox",
"pointable",
"visual",
"visual_size",
"mesh",
"textures",
"colors",
"use_texture_alpha",
"spritediv",
"initial_sprite_basepos",
"is_visible",
"makes_footstep_sound",
"automatic_rotate",
"stepheight",
"automatic_face_movement_dir",
"automatic_face_movement_max_rotation_per_sec",
"backface_culling",
"glow",
"nametag",
"nametag_color",
"nametag_bgcolor",
"infotext",
"static_save",
"damage_texture_modifier",
"shaded",
"show_on_minimap",
}
local player_role_to_image = {
admin = "ch_core_koruna.png",
creative = "ch_core_kouzhul.png",
new = "ch_core_slunce.png",
none = "ch_core_empty.png",
survival = "ch_core_kladivo.png",
}
local wm_to_4dir = {
-- směr "wallmounted" na "4dir" (ztrátová konverze)
[0] = 0, [1] = 0, [2] = 1, [3] = 3, [4] = 0, [5] = 2, [6] = 0, [7] = 0,
}
local wmc_to_4dirc = {
[0] = 0,
[8] = 8,
[16] = 12,
[24] = 20,
[32] = 28,
[40] = 84,
[48] = 76,
[56] = 64,
[64] = 128,
[72] = 132,
[80] = 136,
[88] = 140,
[96] = 144,
[104] = 148,
[112] = 152,
[120] = 156,
[128] = 192,
[136] = 196,
[144] = 200,
[152] = 204,
[160] = 208,
[168] = 212,
[176] = 216,
[184] = 220,
[192] = 224,
[200] = 228,
[208] = 232,
[216] = 236,
[224] = 240,
[232] = 244,
[240] = 248,
[248] = 252,
}
-- KEŠ
-- ===========================================================================
local utf8_sort_cache = {
}
-- LOKÁLNÍ FUNKCE
-- ===========================================================================
local function cmp_oci(a, b)
return (ch_data.offline_charinfo[a].last_login or -1) < (ch_data.offline_charinfo[b].last_login or -1)
end
local function get_player_role_by_privs(privs)
if privs.protection_bypass then
return "admin"
elseif not privs.ch_registered_player then
return "new"
elseif privs.give then
return "creative"
else
return "survival"
end
end
-- VEŘEJNÉ FUNKCE
-- ===========================================================================
--[[
Přidá nástroji opotřebení, pokud „player“ nemá právo usnadnění hry nebo není nil.
]]
function ch_core.add_wear(player, itemstack, wear_to_add)
local player_name = player and player.get_player_name and player:get_player_name()
if not player_name or not minetest.is_creative_enabled(player_name) then
local new_wear = itemstack:get_wear() + wear_to_add
if new_wear > 65535 then
itemstack:clear()
elseif new_wear >= 0 then
itemstack:set_wear(new_wear)
else
itemstack:set_wear(0)
end
end
return itemstack
end
--[[
Sestaví nové pole "groups" následujícím způsobem:
Vezme dvojice z "base"; přepíše je dvojicemi z "inherit" (případně pouze dvojicemi vybranými pomocí "inherit_list")
a nakonec výsledek přepíše dvojicemi z "override". Přitom dodržuje, že pokud nějaké dvojici vyjde hodnota 0,
tato dvojice se na výstupu vůbec neobjeví. Kterýkoliv parametr může být nil; u parametrů base, inherit a override
se to interpretuje jako prázdná tabulka. Je-li inherit_list == nil, pak se z "inherit" vezmou všechny dvojice.
base = table or nil, -- základ, ze kterého se vychází (dvojice s nejnižší prioritou)
override = table or nil, -- dvojice s nejvyšší prioritou (přepíšou vše ostatní)
inherit = table or nil, -- přidat/přepsat tyto hodnoty do base
inherit_list = {string, ...} or nil -- je-li nastaveno, pak se z inherit budou brát pouze klíče z tohoto seznamu a v uvedeném pořadí, jinak všechny klíče
]]
function ch_core.assembly_groups(default, override, inherit, inherit_list)
local result = {}
if default ~= nil then
for k, v in pairs(default) do
if v ~= 0 then
result[k] = v
-- result je prázdný, takže zde není třeba nastavovat nil
end
end
end
if inherit ~= nil then
if inherit_list ~= nil then
for _, group in ipairs(inherit_list) do
local value = inherit[group]
if value ~= nil then
if value ~= 0 then
result[group] = value
else
result[group] = nil
end
end
end
else
for group, value in pairs(inherit) do
if value ~= 0 then
result[group] = value
else
result[group] = nil
end
end
end
end
if override ~= nil then
for group, value in pairs(override) do
if value ~= 0 then
result[group] = value
else
result[group] = nil
end
end
end
return result
end
--[[
Ověří, že předaný argument je funkce a zavolá ji s ostatními zadanými argumenty.
Vrátí její výsledek. Není-li předaný argument funkce, nevrátí nic.
]]
function ch_core.call(f, ...)
if type(f) == "function" then
return f(...)
end
end
--[[
Určí typ postavy (admin|creative|new|survival) a zkontroluje,
zda je to jeden z akceptovaných.
player_or_player_name může být PlayerRef nebo přihlašovací jméno postavy.
accepted_roles může být buď řetězec nebo seznam řetězců.
]]
function ch_core.check_player_role(player_or_player_name, accepted_roles)
local role = ch_core.get_player_role(player_or_player_name)
if role == nil then
return nil
end
if type(accepted_roles) == "string" then
return role == accepted_roles
end
for _, r in ipairs(accepted_roles) do
if role == r then
return true
end
end
return false
end
--[[
Smaže recepty jako minetest.clear_craft(), ale s lepším logováním.
]]
function ch_core.clear_crafts(log_prefix, crafts)
if log_prefix == nil then
log_prefix = ""
else
log_prefix = log_prefix.."/"
end
local get_us_time = minetest.get_us_time
local count = 0
for k, v in pairs(crafts) do
-- minetest.log("action", "Will clear craft "..log_prefix..k)
-- print("CLEAR_CRAFTS("..log_prefix.."): "..dump2(crafts))
if v.output ~= nil or v.type == "fuel" then
if minetest.clear_craft(v) then
count = count + 1
else
minetest.log("warning", "Craft "..log_prefix..k.." not cleared! Dump = "..dump2(v))
end
else
local start = get_us_time()
if minetest.clear_craft(v) then
count = count + 1
local stop = get_us_time()
minetest.log("action", "Craft "..log_prefix..k.." cleared in "..((stop - start) / 1000.0).." ms. Dump = "..dump2(v))
else
minetest.log("warning", "Craft "..log_prefix..k.." not cleared! Dump = "..dump2(v))
end
end
end
return count
end
--[[
Převede param2 z colorwallmounted na color4dir paletu.
]]
function ch_core.colorwallmounted_to_color4dir(param2)
local dir = wm_to_4dir[param2 % 8]
param2 = param2 - param2 % 8
local color = wmc_to_4dirc[param2 - param2 % 8]
return dir + color
end
--[[
Jako vstup přijímá pole dávek (např. z funkce InvRef:get_list())
a vrátí počet prázdných dávek v něm (může být 0).
]]
function ch_core.count_empty_stacks(stacks)
local count = 0
for _, stack in ipairs(stacks) do
if stack:is_empty() then
count = count + 1
end
end
return count
end
--[[
Serializuje pole dávek do řetězce.
Vrací tabulku:
{
success = bool, -- true v případě úspěchu, false v případě selhání
-- v případě úspěchu:
result = string, -- výsledný řetězec (pro prázdný inventář je to prázdný řetězec)
lengths = {int, ...}, -- délky itemstringů jednotlivých stacků
orig_result_length = int, -- délka výsledného řetězce před kompresí
-- v případě selhání:
reason = "single_stack_limit" or "disallow_nested", -- typ selhání
overlimit_index = int or nil, -- v případě selhání "single_stack_limit" index dávky, která překročila limit
overlimit_length = int or nil, -- v případě selhání "single_stack_limit" délku řetězce vráceného to_string()
nested_index = int or nil, -- v případě selhání "disallow_nested" index (první) dávky, která obsahuje vnořený inventář
}
]]
function ch_core.serialize_stacklist(stacks, single_stack_limit, disallow_nested)
if single_stack_limit == nil then
single_stack_limit = 65535
end
local data = {}
local lengths = {}
for i, stack in ipairs(stacks) do
if stack:is_empty(stack) then
data[i] = ""
lengths[i] = 0
else
if disallow_nested then
-- does not contain a nested inventory?
local itemdef = minetest.registered_items[stack:get_name()]
if itemdef ~= nil and itemdef._ch_nested_inventory_meta ~= nil and stack:get_meta():get_string(itemdef._ch_nested_inventory_meta) ~= "" then
minetest.log("action", "Stacklist not serialized because of a nested inventory in "..stack:get_name()..".")
return {
success = false,
reason = "disallow_nested",
nested_index = i,
}
end
end
local s = stack:to_string()
if #s > single_stack_limit then
minetest.log("action", "Stacklist not serialized because of a single stack limit: "..#s.." > "..single_stack_limit..".")
return {
success = false,
reason = "single_stack_limit",
overlimit_index = i,
overlimit_length = #s,
}
end
data[i] = s
lengths[i] = #s
end
end
local orig_str = minetest.serialize(data)
local str = minetest.encode_base64(minetest.compress(orig_str, "deflate"))
minetest.log("action", "Stacklist serialized, resulting length = "..#str..".")
return {
success = true,
result = str,
lengths = lengths,
orig_result_length = #orig_str,
}
end
--[[
Deserializuje řetězec serializovaný pomocí funkce ch_core.serialize_stacklist().
V případě neúspěchu vrátí nil.
]]
function ch_core.deserialize_stacklist(str)
local result = minetest.deserialize(minetest.decompress(minetest.decode_base64(str), "deflate"))
if result ~= nil then
for i = 1, #result do
result[i] = ItemStack(result[i])
end
end
return result
end
--[[
Spočítá a / b a vrátí celočíselný výsledek a zbytek.
]]
function ch_core.divrem(a, b)
local div = math.floor(a / b)
local rem = a % b
return div, rem
end
--[[
Vrátí funkci, která přijme jako první parametr název souboru (či cestu) a pokusí
se ho načíst a spustit jako funkci Lua a vrátit výsledky.
args = table || nil, -- pole parametrů, které mají být předávány volanému souboru,
pokud nejsou žádné parametry specifikovány v rámci volání; žádný z parametrů nesmí být nil!
options = nil || {
path = string || bool || nil,
-- je-li nil nebo true, funkce se pokusí první parametr doplnit o cestu módu, který byl načítán v momentě její konstrukce
-- je-li false, funkce se pokusí použít cestu tak, jak je
-- je-li string, daný řetězec se připojí před zadaný parametr a oddělí "/"
nofail = bool || nil, -- je-li true, funkce bude tiše ignorovat selhání při načítání souboru
}
]]
function ch_core.compile_dofile(args, options)
if args == nil then args = {} end
if options == nil then options = {} end
local modname = minetest.get_current_modname()
local modpath = modname and minetest.get_modpath(modname)
local result = function(name, ...)
local filepath
assert(name)
if options.path == nil or options.path == true then
if modpath == nil then
error("no mod is loading now!")
end
filepath = modpath.."/"..name
elseif options.path == false then
filepath = name
else
filepath = options.path.."/"..name
end
local largs = {...}
if #largs == 0 then
largs = args
end
local f, errmsg = loadfile(filepath)
if f ~= nil then
return f(unpack(largs))
elseif options.nofail == true then
return
else
error("dofile("..filepath..") failed!: "..(errmsg or "nil"))
end
end
return result
end
function ch_core.extend_player_inventory(player_name, extend)
local offline_charinfo = ch_data.offline_charinfo[player_name]
if offline_charinfo == nil then
minetest.log("error", "ch_core.extend_player_inventory() called on player "..player_name.." that has no offline_charinfo!")
return false
end
local player = minetest.get_player_by_name(player_name)
local target_size
if extend then
-- extend the inventory
target_size = ch_core.inventory_size.extended
if player ~= nil then
local inv = player:get_inventory()
local inv_size = inv:get_size("main")
if inv_size < target_size then
inv:set_size("main", target_size)
minetest.log("action", "Player inventory "..player_name.."/main was extended from "..inv_size.." slots to "..target_size..".")
end
end
if offline_charinfo.extended_inventory ~= 1 then
offline_charinfo.extended_inventory = 1
ch_data.save_offline_charinfo(player_name)
end
elseif not extend and offline_charinfo.extended_inventory == 1 then
-- shrink the inventory
target_size = ch_core.inventory_size.normal
if player ~= nil then
local inv = player:get_inventory()
local inv_size = inv:get_size("main")
if inv_size < target_size then
inv:set_size("main", target_size)
minetest.log("action", "Player inventory "..player_name.."/main was extended from "..inv_size.." slots to "..target_size..".")
elseif inv_size > target_size then
local current_items = inv:get_list("main")
local overflown_stacks = {}
inv:set_size("main", target_size)
for i = target_size + 1, inv_size do
local stack = current_items[i]
if not stack:is_empty() then
stack = inv:add_item("main", stack)
if not stack:is_empty() then
table.insert(overflown_stacks, stack)
end
end
end
minetest.log("action", "Player inventory "..player_name.."/main was shrinked from "..inv_size.." slots to "..target_size..". "..#overflown_stacks.." overflown stacks.")
if #overflown_stacks > 0 then
local player_pos = assert(player:get_pos())
for _, stack in ipairs(overflown_stacks) do
if core.add_item(player_pos, stack) == nil then
minetest.log("error", "Spawning overflown item "..stack:to_string().." failed! The item is lost.")
end
end
end
end
end
if offline_charinfo.extended_inventory == 1 then
offline_charinfo.extended_inventory = 0
ch_data.save_offline_charinfo(player_name)
end
else
-- no change
return
end
end
--[[
Pro zadanou hodnotu facedir vrátí rotační vektor symbolizující rotaci
z výchozího otočení (facedir = 0) do otočení cílového.
]]
function ch_core.facedir_to_rotation(facedir)
return vector.copy(facedir_to_rotation_data[facedir % 24])
end
--[[
V zadaném textu odzvláštní všechny znaky, které mají speciální význam
uvnitř prvku hypetext[] ve formspecu. Tato funkce již v sobě zahrnuje
funkci minetest.formspec_escape, takže její obsah by už měl být doslovně
vložen do formspecu bez dalšího zpracování.
]]
function ch_core.formspec_hypertext_escape(text)
local result = string.gsub(text, "[][><\\,;]", hypertext_escape_replacements)
return result
end
--[[
Vrátí seznam všech známých hráčských postav (včetně těch, které nejsou ve hře).
Ke každé postavě vrátí strukturu {prihlasovaci, zobrazovací}.
Seznam je seřazený podle zobrazovacího jména postavy.
-- as_map - je-li true, vrátí seznam (neseřazený) jako mapu z přihlašovacího jména postavy
-- include_privs - je-li true, každý záznam bude navíc obsahovat položky 'role' a 'privs';
tuto variantu nelze volat z inicializačního kódu
]]
function ch_core.get_all_players(as_map, include_privs)
if include_privs and not minetest.get_gametime() then
error("ch_core.get_all_players(): include_privs == true is allowed only when the game is running!")
end
local list, map = {}, {}
for prihlasovaci, _ in pairs(ch_data.offline_charinfo) do
local exists = (not include_privs) or minetest.player_exists(prihlasovaci)
if exists then
local record = {
prihlasovaci = prihlasovaci,
zobrazovaci = ch_core.prihlasovaci_na_zobrazovaci(prihlasovaci),
}
if include_privs then
record.privs = minetest.get_player_privs(prihlasovaci)
record.role = get_player_role_by_privs(record.privs)
end
table.insert(list, record)
map[prihlasovaci] = record
end
end
if as_map then
return map
end
table.sort(list, function(a, b)
return ch_core.utf8_mensi_nez(a.zobrazovaci, b.zobrazovaci, true)
end)
return list
end
--[[
Vrátí seznam všech/jen registrovaných postav, seřazený podle času posledního přihlášení,
od nejčerstvějšího po nejstarší.
registered_only - je-li true, počítá pouze registrované postavy
name_to_skip - je-li nastaveno, postava s daným přihl. jménem se nepočítá
Výsledkem je seznam struktur ve formátu:
{
player_name = STRING, -- přihl. jméno postavy
last_login_before = INT, -- před kolika (kalendářními) dny se postava přihlásila; -1, není-li k dispozici
played_hours_total = FLOAT, -- hodiny ve hře
played_hours_actively = FLOAT, -- aktivně odehrané hodiny ve hře
is_in_game = BOOL, -- je postava aktuálně ve hře?
pending_registration_type = STRING or nil,
is_registered = BOOL,
}
]]
function ch_core.get_last_logins(registered_only, names_to_skip)
local new_players = {} -- new players
local reg_players = {} -- registered players
local shifted_eod = os.time() - 946684800 -- EOD = end of day
shifted_eod = shifted_eod + 86400 - (shifted_eod % 86400)
if names_to_skip == nil then
names_to_skip = {}
elseif type(names_to_skip) == "string" then
names_to_skip = {[names_to_skip] = true}
elseif type(names_to_skip) ~= "table" then
error("names_to_skip: invalid type of argument!")
end
for other_player_name, _ in pairs(ch_data.offline_charinfo) do
if not names_to_skip[other_player_name] then
if minetest.check_player_privs(other_player_name, "ch_registered_player") then
table.insert(reg_players, other_player_name)
elseif not registered_only then
table.insert(new_players, other_player_name)
end
end
end
if registered_only then
new_players = reg_players
table.sort(new_players, cmp_oci)
else
table.sort(new_players, cmp_oci)
table.sort(reg_players, cmp_oci)
table.insert_all(new_players, reg_players)
end
local result = {}
for i = #new_players, 1, -1 do
local other_player_name = new_players[i]
local offline_charinfo = ch_data.offline_charinfo[other_player_name]
local info = {
player_name = other_player_name
}
local last_login = offline_charinfo.last_login
if last_login == 0 then
info.last_login_before = -1
else
info.last_login_before = math.floor((shifted_eod - last_login) / 86400)
end
info.played_hours_total = math.round(offline_charinfo.past_playtime / 36) / 100
info.played_hours_actively = math.round(offline_charinfo.past_ap_playtime / 36) / 100
info.is_in_game = ch_data.online_charinfo[other_player_name] ~= nil
if (offline_charinfo.pending_registration_type or "") ~= "" then
info.pending_registration_type = offline_charinfo.pending_registration_type or ""
end
if minetest.check_player_privs(other_player_name, "ch_registered_player") then
info.is_registered = true
else
info.is_registered = false
end
table.insert(result, info)
end
return result
end
--[[
Najde hráčskou postavu nejbližší k dané pozici. Parametr player_name_to_ignore
je volitelný; je-li vyplněn, má obsahovat přihlašovací jméno postavy
k ignorování.
Vrací „player“ a „player:get_pos()“; v případě neúspěchu vrací nil.
]]
local get_connected_players = minetest.get_connected_players
function ch_core.get_nearest_player(pos, player_name_to_ignore)
local result_player, result_pos, result_distance_2 = 1e+20
for player_name, player in pairs(get_connected_players()) do
if player_name ~= player_name_to_ignore then
local player_pos = player:get_pos()
local x, y, z = player_pos.x - pos.x, player_pos.y - pos.y, player_pos.z - pos.z
local distance_2 = x * x + y * y + z * z
if distance_2 < result_distance_2 then
result_distance_2 = distance_2
result_player = player
result_pos = player_pos
end
end
end
return result_player, result_pos
end
--[[
Vrátí t[k]. Pokud je to nil, přiřadí tam prázdnou tabulku {} a vrátí tu.
]]
function ch_core.get_or_add(t, k)
local result = t[k]
if result == nil then
result = {}
t[k] = result
end
return result
end
--[[
Načte z metadat pozici uloženou pomocí ch_core.set_pos_to_meta().
Není-li tam taková uložena, vrátí vector.zero().
]]
function ch_core.get_pos_from_meta(meta, key)
return vector.new(meta:get_float(key.."_x"), meta:get_float(key.."_y"), meta:get_float(key.."_z"))
end
--[[
Určí podle práv typ postavy (admin|creative|new|survival).
Pokud player_or_player_name není PlayerRef nebo jméno postavy, vrátí nil.
]]
function ch_core.get_player_role(player_or_player_name)
local result = ch_core.normalize_player(player_or_player_name).role
if result ~= "none" then
return result
else
return nil
end
end
--[[
Vrátí seznam hráčských postav (ObjectRef) ve vymezené oblasti.
Jde o bezprostřední náhradu za core.get_objects_inside_radius().
]]
function ch_core.get_players_inside_radius(center, radius)
local result = {}
for _, player in ipairs(core.get_connected_players()) do
if vector.distance(center, player:get_pos()) <= radius then
table.insert(result, player)
end
end
return result
end
--[[
Vygeneruje šablonu pro stránku formspecu pro unified_inventory.
id -- string, required -- rozlišující textové ID pro připojení za prvky ch_scrollbar[12]_
player_viewname -- string, optional -- jméno postavy pro zobrazení v záhlaví (může být barevné)
title -- string, optional -- nadpis pro zobrazení v záhlaví
scrollbars -- table, required -- definuje maxima pro posuvníky oblastí a současně také rozložení oblastí;
tato verze podporuje jen rozložení {left = ..., right = ...} a {top = ..., bottom = ...}
perplayer_formspec -- odpovídající parametr z rozhraní unified_inventory; definuje rozložení formuláře
online_charinfo -- table, optional -- je-li zadáno, stavy posuvníků se nastaví podle stejně pojmenovaných polí v dané tabulce, budou-li přítomna
Výstup má formát:
{
fs_begin, fs_middle, fs_end -- string; řetězce pro použití jako formspec; vlastní obsah vložte kolem fs_middle
form1, form2 = {x, y, w, h, key, scrollbar_max} -- udává pozice a velikost podformulářů v okně unified_inventory;
v praxi jsou podstatné především 'w' a 'h' (šířka a výška podoblasti)
}
]]
function ch_core.get_ui_form_template(id, player_viewname, title, scrollbars, perplayer_formspec, online_charinfo)
local fs = assert(perplayer_formspec)
local fs_begin, fs_middle, fs_end = {fs.standard_inv_bg}, {}, {}
local form1, form2 = {}, {}
local sbar_width = 0.5
local style
if scrollbars.left ~= nil and scrollbars.right ~= nil then
style = "left_right"
elseif scrollbars.top ~= nil and scrollbars.bottom ~= nil then
style = "top_bottom"
else
error("Unsupported UI formspec style!")
end
if style == "left_right" then
form1.x = fs.std_inv_x
form1.y = fs.form_header_y + 0.5
form1.w = 10.0
form1.h = fs.std_inv_y - fs.form_header_y - 1.25
form1.key = "left"
form1.scrollbar_max = scrollbars.left
form2.x = fs.page_x - 0.25
form2.y = 0.5
form2.w = fs.pagecols - 1
form2.h = fs.pagerows - 1 + fs.page_y
form2.key = "right"
form2.scrollbar_max = scrollbars.right
elseif style == "top_bottom" then
form1.x = fs.std_inv_x
form1.y = fs.form_header_y + 0.5
form1.w = 17.25
form1.h = fs.std_inv_y - fs.form_header_y - 1.25
form1.key = "top"
form1.scrollbar_max = scrollbars.top
form2.x = fs.page_x - 0.25
form2.y = fs.std_inv_y - 0.5
form2.w = fs.pagecols - 1
form2.h = 5.5 + 0.5
form2.scrollbar_max = scrollbars.bottom
else
error("not implemented yet")
end
if title ~= nil then
table.insert(fs_begin, "label["..(form1.x + 0.05)..","..(form1.y - 0.3)..";")
if player_viewname ~= nil then
table.insert(fs_begin, minetest.formspec_escape(ch_core.colors.light_green..player_viewname..ch_core.colors.white.." — "))
end
table.insert(fs_begin, minetest.formspec_escape(title))
table.insert(fs_begin, "]")
end
if (form1.scrollbar_max or 0) > 0 then
table.insert(fs_begin, "scroll_container["..form1.x..","..form1.y..";"..form1.w..","..form1.h..";ch_scrollbar1_"..id..";vertical]")
-- CONTENT will be inserted here
table.insert(fs_middle, "scroll_container_end[]")
-- insert a scrollbar
if (form1.scrollbar_max or 0) > 0 then
local scrollbar_state = online_charinfo ~= nil and online_charinfo["ch_scrollbar1_"..id]
if scrollbar_state ~= nil then
scrollbar_state = tostring(scrollbar_state)
else
scrollbar_state = ""
end
table.insert(fs_middle,
"scrollbaroptions[max="..form1.scrollbar_max..";arrows=show]"..
"scrollbar["..(form1.x + form1.w - sbar_width)..","..form1.y..";"..sbar_width..","..form1.h..";vertical;ch_scrollbar1_"..id..";"..
scrollbar_state.."]")
end
else
table.insert(fs_begin, "container["..form1.x..","..form1.y.."]")
-- CONTENT will be inserted here
table.insert(fs_middle, "container_end[]")
end
if (form2.scrollbar_max or 0) > 0 then
table.insert(fs_middle, "scroll_container["..form2.x..","..form2.y..";"..form2.w..","..form2.h..";ch_scrollbar2_"..id..";vertical]")
-- CONTENT will be inserted here
table.insert(fs_end, "scroll_container_end[]")
-- insert a scrollbar
if (form2.scrollbar_max or 0) > 0 then
local scrollbar_state = online_charinfo ~= nil and online_charinfo["ch_scrollbar2_"..id]
if scrollbar_state ~= nil then
scrollbar_state = tostring(scrollbar_state)
else
scrollbar_state = ""
end
table.insert(fs_end,
"scrollbaroptions[max="..form2.scrollbar_max..";arrows=show]"..
"scrollbar["..(form2.x + form2.w - sbar_width)..","..form2.y..";"..sbar_width..","..form2.h..";vertical;ch_scrollbar2_"..id..";"..
scrollbar_state.."]")
end
else
table.insert(fs_middle, "container["..form2.x..","..form2.y.."]")
-- CONTENT will be inserted here
table.insert(fs_end, "container_end[]")
end
return {
fs_begin = table.concat(fs_begin),
fs_middle = table.concat(fs_middle),
fs_end = table.concat(fs_end),
form1 = form1,
form2 = form2,
}
end
--[[
Vrátí t[k1]. Pokud je to nil, vyplní t[k1] = {} a vrátí přiřazenou tabulku.
]]
function ch_core.goa1(t, k)
local r = t[k]
if r ~= nil then return r end
r = {}
t[k] = r
return r
end
--[[
Vrátí t[k1][k2]. Pokud některá z položek chybí, vyplní ji novou prázdnou tabulkou.
]]
function ch_core.goa2(t, k1, k2)
local r1 = t[k1]
if r1 == nil then
r1 = {}
t[k1] = {[k2] = r1}
return r1
end
local r2 = r1[k2]
if r2 == nil then
r2 = {}
r1[k2] = r2
return r2
end
return r2
end
local goa2 = ch_core.goa2
--[[
Vrátí t[k1][k2][k3]. Pokud některá z položek chybí, vyplní ji novou prázdnou tabulkou.
]]
function ch_core.goa3(t, k1, k2, k3)
local r = t[k1]
if r ~= nil then
return goa2(r, k2, k3)
else
r = {}
t[k1] = {[k2] = {[k3] = r}}
return r
end
end
--[[
Vrátí t[k1][k2][k3][k4]. Pokud některá z položek chybí, vyplní ji novou prázdnou tabulkou.
]]
function ch_core.goa4(t, k1, k2, k3, k4)
return goa2(goa2(t, k1, k2), k3, k4)
end
--[[
Jednoduchá funkce, která vyhodnotí condition jako podmínku
a podle výsledku vrátí buď true_result, nebo false_result.
]]
function ch_core.ifthenelse(condition, true_result, false_result)
if condition then
return true_result
else
return false_result
end
end
--[[
Vrací funkci function(itemstack, user, pointed_thing),
která zavolá do_item_eat() podle hodnoty skupiny ch_food,
drink nebo ch_poison u daného předmětu. Není-li předmět v těchto
skupinách, vrátí nil.
]]
local item_eat_cache = {}
function ch_core.item_eat(replace_with_item)
if replace_with_item == nil then
replace_with_item = ""
elseif type(replace_with_item) ~= "string" then
error("replace_with_item must be string or nil")
end
local result = item_eat_cache[replace_with_item]
if result == nil then
result = function(itemstack, user, pointed_thing)
local name = itemstack:get_name()
if name == nil or name == "" or minetest.registered_items[name] == nil then return end
local food = minetest.get_item_group(name, "ch_food")
local drink = minetest.get_item_group(name, "drink")
local poison = minetest.get_item_group(name, "ch_poison")
local health
if poison ~= 0 then
local normal = math.max(food, drink)
if normal ~= 0 and math.random(5) ~= 3 then
health = normal
else
health = -poison
end
else
health = math.max(food, drink)
if health <= 0 then
return
end
end
return minetest.do_item_eat(health, replace_with_item, itemstack, user, pointed_thing)
end
item_eat_cache[replace_with_item] = result
end
return result
end
--[[
Vytvoří pomocnou strukturu pro položku dropdown[] ve formspecu.
Pomocná struktura obsahuje položky:
- function get_index_from_value(value, default_index)
- function get_value_from_index(index, default_index)
- table index_to_value // původní předaná tabulka (musí být sekvence)
- string formspec_list // již odzvláštněný seznam k použití ve formspecu
- table value_to_index // mapuje text položky na první odpovídající index
]]
function ch_core.make_dropdown(index_to_value)
local F = minetest.formspec_escape
local escaped_values = {}
local value_to_index = {}
for i, value in ipairs(index_to_value) do
escaped_values[i] = F(value)
end
for i = #index_to_value, 1, -1 do
value_to_index[index_to_value[i]] = i
end
return {
get_index_from_value = function(value, default_index)
if value ~= nil then
return value_to_index[value] or tonumber(default_index)
else
return tonumber(default_index)
end
end,
get_value_from_index = function(index, default_index)
index = tonumber(index)
if index ~= nil and index_to_value[index] ~= nil then
return index_to_value[index]
else
return index_to_value[default_index]
end
end,
index_to_value = index_to_value,
formspec_list = table.concat(escaped_values, ","),
value_to_index = value_to_index,
}
end
--[[
Přijme parametr, kterým může být:
a) přihlašovací jméno postavy (bez ohledu na diakritiku)
b) zobrazovací jméno postavy
c) objekt postavy
d) tabulka s volatelnou funkcí get_player_name()
Ve všech případech vrátí tabulku s prvky:
{
role, -- role postavy nebo "none", pokud neexistuje
player_name, -- skutečné přihlašovací jméno existující postavy, nebo "", pokud postava neexistuje; nikdy nebude nil
viewname, -- zobrazovací jméno postavy (bez barev), nebo "", pokud postava neexistuje; nikdy nebude nil
player, -- je-li postava ve hře, PlayerRef, jinak nil
privs, -- tabulka práv postavy; pokud neexistuje, {}; nikdy nebude nil
}
]]
function ch_core.normalize_player(player_name_or_player)
local arg_type = type(player_name_or_player)
local player_name, player
if arg_type == "string" then
player_name = player_name_or_player
elseif arg_type == "number" then
player_name = tostring(player_name_or_player)
elseif (arg_type == "table" or arg_type == "userdata") and type(player_name_or_player.get_player_name) == "function" then
player_name = player_name_or_player:get_player_name()
if type(player_name) ~= "string" then
player_name = ""
else
if minetest.is_player(player_name_or_player) then
player = player_name_or_player
end
end
else
player_name = ""
end
player_name = ch_core.jmeno_na_prihlasovaci(player_name)
local correct_name = ch_data.correct_player_name_casing(player_name)
if correct_name ~= nil then
player_name = correct_name
end
if player_name ~= "" and not minetest.player_exists(player_name) then
player_name = ch_core.jmeno_na_prihlasovaci(player_name)
if not minetest.player_exists(player_name) then
player_name = ""
end
end
if player_name == "" then
return {role = "none", player_name = "", viewname = "", privs = {}}
end
local privs = minetest.get_player_privs(player_name)
return {
role = get_player_role_by_privs(privs),
player_name = player_name,
viewname = ch_core.prihlasovaci_na_zobrazovaci(player_name),
privs = privs,
player = player or minetest.get_player_by_name(player_name),
}
end
--[[
Vytvoří kopii vstupu (input) a zapíše do ní nové hodnoty skupin podle
parametru override. Skupiny s hodnotou 0 v override z tabulky odstraní.
Je-li některý z parametrů nil, je interpretován jako prázdná tabulka.
ZASTARALÁ: použijte raději ch_core.assembly_groups().
]]
function ch_core.override_groups(input, override)
return ch_core.assembly_groups(input, override)
end
--[[
Převede zobrazovací nebo přihlašovací jméno na přihlašovací jméno,
bez ohledu na to, zda takové jméno existuje.
]]
function ch_core.jmeno_na_prihlasovaci(jmeno)
return ch_core.odstranit_diakritiku(jmeno):gsub(" ", "_")
end
--[[
Vrátí existující přihlašovací jméno postavy odpovídající uvedenému
jménu až na velikost písmen a diakritiku (+ konverzi ' ' na '_'), nebo nil, pokud
taková postava neexistuje.
]]
function ch_core.jmeno_na_existujici_prihlasovaci(jmeno)
if jmeno == nil then return nil end
local result = ch_core.odstranit_diakritiku(jmeno):gsub(" ", "_")
result = ch_data.correct_player_name_casing(result)
if result and ch_data.offline_charinfo[result] then
return result
else
return nil
end
end
--[[
Převede všechna písmena v řetězci na malá, funguje i na písmena s diakritikou.
]]
function ch_core.na_mala_pismena(s)
local l = #s
local i = 1
local res = ""
local c
while i <= l do
c = diakritika_na_mala[s:sub(i, i + 1)]
if c then
res = res .. c
i = i + 2
else
res = res .. s:sub(i, i)
i = i + 1
end
end
return string.lower(res)
end
--[[
Převede všechna písmena v řetězci na velká, funguje i na písmena s diakritikou.
]]
function ch_core.na_velka_pismena(s)
local l = #s
local i = 1
local res = ""
local c
while i <= l do
c = diakritika_na_velka[s:sub(i, i + 1)]
if c then
res = res .. c
i = i + 2
else
res = res .. s:sub(i, i)
i = i + 1
end
end
return string.upper(res)
end
--[[
Vrátí počet bloků uvnitř oblasti vymezené dvěma krajními body (na pořadí nezáleží).
Výsledkem je vždy kladné celé číslo.
]]
function ch_core.objem_oblasti(pos1, pos2)
return math.ceil(math.abs(pos1.x - pos2.x) + 1) * math.ceil(math.abs(pos1.y - pos2.y) + 1) * math.ceil(math.abs(pos1.z - pos2.z) + 1)
end
--[[
Všechna písmena s diakritikou převede na odpovídající písmena bez diakritiky.
Ostatní znaky ponechá.
]]
function ch_core.odstranit_diakritiku(s)
local l = #s
local i = 1
local res = ""
local c
while i <= l do
c = diakritika[s:sub(i, i + 1)]
if c then
res = res .. c
i = i + 2
else
res = res .. s:sub(i, i)
i = i + 1
end
end
return res
end
--[[
Pokud je zadaný klient ve hře (musí jít o přihlašovací jméno), přehraje mu zvuk kliknutí.
Parametr může být nil; v takovém případě neudělá nic.
]]
function ch_core.play_click_sound_to(player_name)
if player_name ~= nil then
minetest.sound_play(click_sound, {to_player = player_name}, true)
end
end
--[[
K zadané roli postavy vrátí odpovídající ikonu.
]]
function ch_core.player_role_to_image(player_role, has_creative_priv, image_height)
if image_height ~= nil and image_height ~= 32 and image_height ~= 16 then
error("ch_core.player_role_to_image(): image height "..image_height.." is unsupported!")
end
local result = assert(player_role_to_image[player_role] or player_role_to_image["none"])
if player_role ~= "new" and has_creative_priv then
result = "[combine:48x32:0,0="..result..":16,0="..player_role_to_image.creative
if image_height == 16 then
result = result.."^[resize:24x16"
end
elseif image_height == 16 then
result = result.."^[resize:16x16"
end
return result
end
--[[
Otestuje, zda pozice „pos“ leží uvnitř oblasti vymezené v_min a v_max.
]]
function ch_core.pos_in_area(pos, v_min, v_max)
return v_min.x <= pos.x and pos.x <= v_max.x and
v_min.y <= pos.y and pos.y <= v_max.y and
v_min.z <= pos.z and pos.z <= v_max.z
end
--[[
vrátí dva vektory: první s minimálními souřadnicemi a druhý s maximálními,
obojí zaokrouhlené na celočíselné souřadnice
]]
function ch_core.positions_to_area(v1, v2)
local x1, x2, y1, y2, z1, z2
if v1.x <= v2.x then
x1 = v1.x
x2 = v2.x
else
x1 = v2.x
x2 = v1.x
end
if v1.y <= v2.y then
y1 = v1.y
y2 = v2.y
else
y1 = v2.y
y2 = v1.y
end
if v1.z <= v2.z then
z1 = v1.z
z2 = v2.z
else
z1 = v2.z
z2 = v1.z
end
return vector.round(vector.new(x1, y1, z1)), vector.round(vector.new(x2, y2, z2))
end
--[[
Pokud dané přihlašovací jméno existuje, převede ho na jméno bez barev (výchozí)
nebo s barvami. Pro neexistující jména vrací zadaný řetězec.
]]
function ch_core.prihlasovaci_na_zobrazovaci(prihlasovaci, s_barvami)
local offline_info, jmeno
if not prihlasovaci then
error("ch_core.prihlasovaci_na_zobrazovaci() called with bad arguments!")
end
if minetest.player_exists(prihlasovaci) then
offline_info = ch_data.offline_charinfo[prihlasovaci] or {}
if s_barvami then
jmeno = offline_info.barevne_jmeno
if jmeno then return jmeno end
end
jmeno = offline_info.jmeno
if jmeno then return jmeno end
end
return prihlasovaci
end
--[[
Zaregistruje bloky, které mají něco společného.
]]
function ch_core.register_nodes(common_def, nodes, crafts)
if type(common_def) ~= "table" then
error("common_def must be a table!")
end
if type(nodes) ~= "table" then
error("nodes must be a table!")
end
if crafts ~= nil and type(crafts) ~= "table" then
error("crafts must be a table or nil!")
end
for node_name, node_def in pairs(nodes) do
local def = table.copy(common_def)
for k, v in pairs(node_def) do
def[k] = v
end
minetest.register_node(node_name, def)
end
if crafts ~= nil then
for _, def in ipairs(crafts) do
minetest.register_craft(def)
end
end
end
--[[
Smaže data týkající se dané postavy.
]]
function ch_core.remove_player(player_name, options)
if options == nil then
options = {}
end
player_name = ch_core.jmeno_na_prihlasovaci(player_name)
if not minetest.player_exists(player_name) then
return false, player_name.." neexistuje!"
end
local results = {player_name.." odstraněn/a:"}
-- remove player data
if options.player_data ~= false then
if minetest.remove_player(player_name) == 0 then
table.insert(results, "player_data")
end
end
-- remove offline charinfo
local f = ch_core.delete_offline_charinfo
if options.offline_charinfo ~= false and f ~= nil then
if f(player_name) then
table.insert(results, "offline_charinfo")
end
end
-- remove auth data
if options.player_auth ~= false then
if minetest.remove_player_auth(player_name) then
table.insert(results, "player_auth")
end
end
return true, table.concat(results, " ")
end
--[[
Otočí axis-aligned bounding box o zadané otočení. Nejde-li o pravoúhlé
otočení, výsledný kvádr bude větší.
]]
function ch_core.rotate_aabb(aabb, r)
local points = {}
for _, x in ipairs({r[1], r[4]}) do
for _, y in ipairs({r[2], r[5]}) do
for _, z in ipairs({r[3], r[6]}) do
table.insert(points, vector.rotate(vector.new(x, y, z), r))
end
end
end
local p = points[1]
local result = {p.x, p.y, p.z, p.x, p.y, p.z}
for i = 2, 8 do
p = points[i]
if p.x < result[1] then
result[1] = p.x
elseif p.x > result[4] then
result[4] = p.x
end
if p.y < result[2] then
result[2] = p.y
elseif p.y > result[5] then
result[5] = p.y
end
if p.z < result[3] then
result[3] = p.z
elseif p.z > result[6] then
result[6] = p.z
end
end
return result
end
--[[
Otočí axis-aligned bounding box takovým způsobem, jako zadaná facedir-hodnota 0 až 23
otočí blok s paramtype2 == "facedir". Vrátí nový aabb. V případě selhání vrátí nil.
]]
function ch_core.rotate_aabb_by_facedir(aabb, facedir)
if 0 <= facedir and facedir < 24 and facedir_to_rotation_data[facedir] then
local a = vector.new(aabb[1], aabb[2], aabb[3])
local b = vector.new(aabb[4], aabb[5], aabb[6])
local r = facedir_to_rotation_data[facedir]
a = vector.rotate(a, r)
b = vector.rotate(b, r)
return {
math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z),
math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z),
}
else
return nil
end
end
--[[
Provede operaci t[k1][k2]... s tím, že pokud je kterýkoliv z parametrů nil
nebo na nil po cestě narazí, vrátí nil. Číslo v názvu udává celkový počet
parametrů (včetně t) a musí být v rozsahu 2 až 7 včetně.
]]
function ch_core.safe_get_2(t, k1)
if t and k1 ~= nil then return t[k1] end
return nil
end
function ch_core.safe_get_3(t, k1, k2)
local result
if t and k1 ~= nil and k2 ~= nil then
result = t[k1]
if result ~= nil then
return result[k2]
end
end
return nil
end
function ch_core.safe_get_4(t, k1, k2, k3)
local result
if t and k1 ~= nil and k2 ~= nil and k3 ~= nil then
result = t[k1]
if result ~= nil then
result = result[k2]
if result ~= nil then
return result[k3]
end
end
end
return nil
end
function ch_core.safe_get_5(t, k1, k2, k3, k4)
local result
if t and k1 ~= nil and k2 ~= nil and k3 ~= nil and k4 ~= nil then
result = t[k1]
if result ~= nil then
result = result[k2]
if result ~= nil then
result = result[k3]
if result ~= nil then
return result[k4]
end
end
end
end
return nil
end
function ch_core.safe_get_6(t, k1, k2, k3, k4, k5)
local result
if t and k1 ~= nil and k2 ~= nil and k3 ~= nil and k4 ~= nil and k5 ~= nil then
result = t[k1]
if result ~= nil then
result = result[k2]
if result ~= nil then
result = result[k3]
if result ~= nil then
result = result[k4]
if result ~= nil then
return result[k5]
end
end
end
end
end
return nil
end
function ch_core.safe_get_7(t, k1, k2, k3, k4, k5, k6)
local result
if t and k1 ~= nil and k2 ~= nil and k3 ~= nil and k4 ~= nil and k5 ~= nil and k6 ~= nil then
result = t[k1]
if result ~= nil then
result = result[k2]
if result ~= nil then
result = result[k3]
if result ~= nil then
result = result[k4]
if result ~= nil then
result = result[k5]
if result ~= nil then
return result[k6]
end
end
end
end
end
end
return nil
end
--[[
Provede operaci t[k1][k2]... s tím, že pokud je kterýkoliv z parametrů nil
nebo na nil po cestě narazí, vrátí nil. Číslo v názvu udává celkový počet
parametrů (včetně t) a musí být v rozsahu 2 až 7 včetně.
V této verzi knihovny neprovádí kontrolu, zda je t indexovatelné.
]]
function ch_core.safe_get_2(t, k1)
if t and k1 ~= nil then return t[k1] end
return nil
end
function ch_core.safe_get_3(t, k1, k2)
local result
if t and k1 ~= nil and k2 ~= nil then
result = t[k1]
if result ~= nil then
return result[k2]
end
end
return nil
end
function ch_core.safe_get_4(t, k1, k2, k3)
local result
if t and k1 ~= nil and k2 ~= nil and k3 ~= nil then
result = t[k1]
if result ~= nil then
result = result[k2]
if result ~= nil then
return result[k3]
end
end
end
return nil
end
function ch_core.safe_get_5(t, k1, k2, k3, k4)
if k4 ~= nil then
local result = ch_core.safe_get_4(t, k1, k2, k3)
if result ~= nil then
return result[k4]
end
end
return nil
end
function ch_core.safe_get_6(t, k1, k2, k3, k4, k5)
if k4 ~= nil and k5 ~= nil then
local result = ch_core.safe_get_4(t, k1, k2, k3)
if result ~= nil then
result = result[k4]
if result ~= nil then
return result[k5]
end
end
end
return nil
end
function ch_core.safe_get_7(t, k1, k2, k3, k4, k5, k6)
if k4 ~= nil and k5 ~= nil and k6 ~= nil then
local result = ch_core.safe_get_4(t, k1, k2, k3)
if result ~= nil then
result = result[k4]
if result ~= nil then
result = result[k5]
if result ~= nil then
return result[k6]
end
end
end
end
return nil
end
--[[
Provede operaci t[k1][k2]... = v s tím, že pokud je kterýkoliv z parametrů
kromě „v“ nil nebo na nil po cestě narazí, vrátí false.
Pokud přiřazení uspěje, vrátí true.
Číslo v názvu udává celkový počet parametrů (včetně t a v)
a musí být v rozsahu 3 až 8 včetně.
V této verzi knihovny neprovádí kontrolu, zda je t indexovatelné.
]]
function ch_core.safe_set_3(t, k1, v)
if t and k1 ~= nil then
t[k1] = v
return true
end
return false
end
function ch_core.safe_set_4(t, k1, k2, v)
if k2 ~= nil then
t = ch_core.safe_get_2(t, k1)
if t then
t[k2] = v
return true
end
end
return false
end
function ch_core.safe_set_5(t, k1, k2, k3, v)
if k3 ~= nil then
t = ch_core.safe_get_3(t, k1, k2)
if t then
t[k3] = v
return true
end
end
return false
end
function ch_core.safe_set_6(t, k1, k2, k3, k4, v)
if k4 ~= nil then
t = ch_core.safe_get_4(t, k1, k2, k3)
if t then
t[k4] = v
return true
end
end
return false
end
function ch_core.safe_set_7(t, k1, k2, k3, k4, k5, v)
if k5 ~= nil then
t = ch_core.safe_get_5(t, k1, k2, k3, k4)
if t then
t[k5] = v
return true
end
end
return false
end
function ch_core.safe_set_8(t, k1, k2, k3, k4, k5, k6, v)
if k6 ~= nil then
t = ch_core.safe_get_6(t, k1, k2, k3, k4, k5)
if t then
t[k6] = v
return true
end
end
return false
end
--[[
Nastaví dané postavě status „immortal“. Používá se pro postavy s právem
usnadnění hry.
]]
function ch_core.set_immortal(player, true_or_false)
if true_or_false then
local properties = player:get_properties()
player:set_armor_groups({immortal = 1})
player:set_hp(properties.hp_max)
player:set_breath(properties.breath_max)
else
player:set_armor_groups({immortal = 0})
end
return true
end
--[[
Uloží do metadat souřadnice x, y, z včetně desetinné části.
]]
function ch_core.set_pos_to_meta(meta, key, pos)
local x_key, y_key, z_key = key.."_x", key.."_y", key.."_z"
meta:set_float(x_key, pos.x)
meta:set_float(y_key, pos.y)
meta:set_float(z_key, pos.z)
local stored_pos = vector.new(meta:get_float(x_key), meta:get_float(y_key), meta:get_float(z_key))
if not vector.equals(pos, stored_pos) then
minetest.log("warning", "Position truncated when stored to metadata: "..vector.to_string(pos).." truncated to: "..vector.to_string(stored_pos))
end
return pos
end
--[[
Přesune klíče definující vlastnosti entity do podtabulky initial_properties.
Provádí úpravy přímo v předané tabulce a vrací ji (tzn. nevytváří kopii).
]]
function ch_core.upgrade_entity_properties(entity_def, options)
if options == nil then
options = {}
end
local base_properties = options.base_properties -- základ pro doplnění zcela chybějících vlastností
local in_place = options.in_place ~= false -- provádět změny v původní tabulce initial_properties, je-li dostupná; výchozí: true
local keep_fields = options.keep_fields == true -- ponechat původní pole v původní tabulce; výchozí: false
local initial_properties
if entity_def.initial_properties == nil then
initial_properties = {}
elseif in_place then
initial_properties = entity_def.initial_properties
else
initial_properties = table.copy(entity_def.initial_properties)
end
for _, k in ipairs(entity_properties_list) do
if entity_def[k] ~= nil then
if initial_properties[k] == nil then
initial_properties[k] = entity_def[k]
end
if not keep_fields then
entity_def[k] = nil
end
end
end
if base_properties ~= nil then
for k, v in pairs(base_properties) do
if initial_properties[k] == nil then
initial_properties[k] = v
end
end
end
entity_def.initial_properties = initial_properties
return entity_def
end
--[[
Vrátí počet UTF-8 znaků řetězce.
]]
function ch_core.utf8_length(s)
if s == "" then
return 0
end
local i, byte, bytes, chars
i = 1
chars = 0
bytes = string.len(s)
while i <= bytes do
byte = string.byte(s, i)
if byte < 192 then
i = i + 1
else
i = i + utf8_charlen[byte]
end
chars = chars + 1
end
return chars
end
--[[
Začne v řetězci `s` na fyzickém indexu `i` a bude se posouvat o `seek`
UTF-8 znaků doprava (pro záporný počet doleva); vrátí výsledný index
(na první bajt znaku), nebo nil, pokud posun přesáhl začátek,
resp. konec řetězce.
]]
function ch_core.utf8_seek(s, i, seek)
local bytes = string.len(s)
if i < 1 or i > bytes then
return nil
end
local b
if seek > 0 then
while true do
b = string.byte(s, i)
if b < 192 then
i = i + 1
else
i = i + utf8_charlen[b]
end
if i > bytes then
return nil
end
seek = seek - 1
if seek == 0 then
return i
end
end
elseif seek < 0 then
while true do
i = i - 1
if i < 1 then
return nil
end
b = string.byte(s, i)
if b < 128 or b >= 192 then
-- máme další znak
seek = seek + 1
if seek == 0 then
return i
end
end
end
else
return i
end
end
--[[
Je-li řetězec s delší než max_chars znaků, vrátí jeho prvních max_chars znaků
+ "...", jinak vrátí původní řetězec.
]]
function ch_core.utf8_truncate_right(s, max_chars, dots_string)
local i = ch_core.utf8_seek(s, 1, max_chars)
if i then
return s:sub(1, i - 1) .. (dots_string or "...")
else
return s
end
end
--[[
Rozdělí řetězec na pole neprázdných podřetězců o stanovené maximální délce
v UTF-8 znacích; v každé části vynechává mezery na začátku a na konci části;
přednostně dělí v místech mezer. Pro prázdný řetězec
(nebo řetězec tvořený jen mezerami) vrací prázdné pole.
]]
function ch_core.utf8_wrap(s, max_chars, options)
local i = 1 -- index do vstupního řetězce s
local s_bytes = string.len(s)
local result = {} -- výstupní pole
local r_text = "" -- výstupní řetězec
local r_chars = 0 -- počet UTF-8 znaků v řetězci r
local r_sp_begin -- index první mezery v poslední sekvenci mezer v r_text
local r_sp_end -- index poslední mezery v poslední sekvenci mezer v r_text
local b -- kód prvního bajtu aktuálního znaku
local c_bytes -- počet bajtů aktuálního znaku
-- options
local allow_empty_lines, max_result_lines, line_separator
if options then
allow_empty_lines = options.allow_empty_lines -- true or false
max_result_lines = options.max_result_lines -- nil or number
line_separator = options.line_separator -- nil or string
end
while i <= s_bytes do
b = string.byte(s, i)
-- print("byte["..i.."] = "..b.." ("..s:sub(i, i)..") r_sp = ("..(r_sp_begin or "nil")..".."..(r_sp_end or "nil")..")")
if r_chars > 0 or (b ~= 32 and (b ~= 10 or allow_empty_lines)) then -- na začátku řádky ignorovat mezery
if b < 192 then
c_bytes = 1
else
c_bytes = utf8_charlen[b]
end
-- vložit do r další znak (není-li to konec řádky)
if b ~= 10 then
r_text = r_text..s:sub(i, i + c_bytes - 1)
r_chars = r_chars + 1
if b == 32 then
-- znak je mezera
if r_sp_begin then
if r_sp_end then
-- začátek nové skupiny mezer (už nějaká byla)
r_sp_begin = string.len(r_text)
r_sp_end = nil
end
elseif not r_sp_end then
-- začátek první skupiny mezer (ještě žádná nebyla)
r_sp_begin = string.len(r_text)
end
else
-- znak není mezera ani konec řádky
if r_sp_begin and not r_sp_end then
r_sp_end = string.len(r_text) - c_bytes -- uzavřít skupinu mezer
end
end
end
if r_chars >= max_chars or b == 10 then
-- dosažen maximální počet znaků nebo znak \n => uzavřít řádku
if line_separator and #result > 0 then
result[#result] = result[#result]..line_separator
end
if r_chars < max_chars or not r_sp_begin then
-- žádné mezery => tvrdé dělení
table.insert(result, r_text)
r_text = ""
r_chars = 0
r_sp_begin, r_sp_end = nil, nil
elseif not r_sp_end then
-- průběžná skupina mezer => rozdělit zde
table.insert(result, r_text:sub(1, r_sp_begin - 1))
r_text = ""
r_chars = 0
r_sp_begin, r_sp_end = nil, nil
else
-- byla skupina mezer => rozdělit tam
table.insert(result, r_text:sub(1, r_sp_begin - 1))
r_text = r_text:sub(r_sp_end + 1, -1)
r_chars = ch_core.utf8_length(r_text)
r_sp_begin, r_sp_end = nil, nil
if r_chars > 0 and b == 10 then
i = i - c_bytes -- read this \n-byte again
end
end
if max_result_lines and #result >= max_result_lines then
return result -- skip reading other lines
end
end
i = i + c_bytes
else
i = i + 1
end
end
if r_chars > 0 then
if line_separator and #result > 0 then
result[#result] = result[#result]..line_separator
end
if r_sp_begin and not r_sp_end then
-- průběžná skupina mezer
table.insert(result, r_text:sub(1, r_sp_begin - 1))
else
table.insert(result, r_text)
end
end
return result
end
function ch_core.utf8_radici_klic(s, store_to_cache)
local result = utf8_sort_cache[s]
if not result then
local i = 1
local l = s:len()
local c, k
result = {}
while i <= l do
c = s:sub(i, i)
k = utf8_sort_data_1[c]
if k then
table.insert(result, k)
i = i + 1
else
k = utf8_sort_data_2[s:sub(i, i + 1)]
if k then
table.insert(result, k)
i = i + 2
else
k = utf8_sort_data_3[c]
table.insert(result, k or c)
i = i + 1
end
end
end
result = table.concat(result)
if store_to_cache then
utf8_sort_cache[s] = result
end
end
return result
end
function ch_core.utf8_mensi_nez(a, b, store_to_cache)
a = ch_core.utf8_radici_klic(a, store_to_cache)
b = ch_core.utf8_radici_klic(b, store_to_cache)
return a < b
end
--[[
-- KÓD INICIALIZACE
-- ===========================================================================
local dbg_table = ch_core.storage:to_table()
if not dbg_table then
print("STORAGE: nil")
else
for key, value in pairs(dbg_table.fields) do
print("STORAGE: <"..key..">=<"..value..">")
end
end
]]
doors.login_to_viewname = ch_core.prihlasovaci_na_zobrazovaci
doors.viewname_to_login = ch_core.jmeno_na_prihlasovaci
-- PŘÍKAZY
-- ===========================================================================
def = {
description = "Vypíše seznam neregistrovaných postav seřazený podle času posledního přihlášení.",
privs = {server = true},
func = function(player_name, param)
local last_logins = ch_core.get_last_logins(false)
local result = {}
for i = #last_logins, 1, -1 do
local info = last_logins[i]
local viewname = ch_core.prihlasovaci_na_zobrazovaci(info.player_name)
local s = "- "..viewname.." (posl. přihl. před "..info.last_login_before.." dny, odehráno "..info.played_hours_total..
" hodin, z toho "..info.played_hours_actively.." aktivně)"
if info.is_in_game then
s = s.." "
end
if info.pending_registration_type ~= nil then
s = s.." "
end
if info.is_registered then
s = s.." "
end
table.insert(result, s)
end
result = table.concat(result, "\n")
minetest.log("warning", result)
minetest.chat_send_player(player_name, result)
return true
end,
}
minetest.register_chatcommand("postavynauklid", def)
minetest.register_chatcommand("postavynaúklid", def)
ch_core.close_submod("lib")