aboutsummaryrefslogtreecommitdiff
path: root/ch_core/teleportace.lua
diff options
context:
space:
mode:
Diffstat (limited to 'ch_core/teleportace.lua')
-rw-r--r--ch_core/teleportace.lua578
1 files changed, 578 insertions, 0 deletions
diff --git a/ch_core/teleportace.lua b/ch_core/teleportace.lua
new file mode 100644
index 0000000..f4c4003
--- /dev/null
+++ b/ch_core/teleportace.lua
@@ -0,0 +1,578 @@
+ch_core.open_submod("teleportace", {privs = true, data = true, chat = true, lib = true, stavby = true, timers = true})
+
+local ifthenelse = ch_core.ifthenelse
+
+local can_teleport_callbacks = {}
+
+-- LOCAL FUNCTIONS
+local function get_entity_name(obj)
+ if obj.get_luaentity then
+ obj = obj:get_luaentity()
+ end
+ return obj.name or "unknown entity"
+end
+
+local function get_finish_teleport_func(player_name, d, is_immediate)
+ return function()
+ -- je postava stále ještě ve hře?
+ local player = minetest.get_player_by_name(player_name)
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if player == nil or online_charinfo == nil then
+ return
+ end
+ local player_pos = player:get_pos()
+ player:set_properties{pointable = true}
+ ch_core.set_temporary_titul(player_name, "přemísťuje se", false)
+
+ -- zopakovat testy přemístitelnosti
+ if not is_immediate then
+ if d.type ~= "admin" and ch_core.je_ve_vykonu_trestu ~= nil then
+ local trest = ch_core.je_ve_vykonu_trestu(player_name)
+ if trest ~= nil then
+ ch_core.systemovy_kanal(player_name, "Jste ve výkonu trestu odnětí svobody! Zbývající výše trestu: "..trest)
+ return false
+ end
+ end
+ end
+
+ -- can_teleport?
+ local priority
+ if d.type == "admin" then
+ priority = 4
+ elseif d.type == "force" then
+ priority = 3
+ else
+ priority = 2
+ end
+ for name, can_teleport in pairs(can_teleport_callbacks) do
+ if can_teleport(player, online_charinfo, priority) == false then
+ minetest.log("warning", "teleport_player() denied due to callback "..name)
+ return false, "Přemístění selhalo (technické informace: "..name..")"
+ end
+ end
+
+ -- přehrát zvuk před přemístěním
+ if d.sound_before ~= nil then
+ minetest.sound_play(d.sound_before, {pos = player_pos, max_hear_distance = d.sound_before_max_hearing_distance or 50.0})
+ end
+
+ -- zavolat funkce před přemístěním
+ if d.callback_before ~= nil then
+ d.callback_before()
+ end
+
+ -- zavříŧ formspec
+ if d.close_formspec == true then
+ minetest.close_formspec(player_name, "")
+ end
+
+ -- vyřešit připojení postavy k objektu
+ local attach_object = player:get_attach()
+ if attach_object ~= nil then
+ local entity_name = get_entity_name(attach_object)
+
+ if (d.type == "admin" or d.type == "force") and entity_name == "boats:boat" then
+ -- detach from the boat
+ player:set_detach()
+ player_api.player_attached[player_name] = false
+ player_api.set_animation(player, "stand" , 30)
+ else
+ if d.type == "player" then
+ ch_core.systemovy_kanal(player_name, "Přemístění selhalo")
+ end
+ minetest.log("action", player_name.." was not teleported to "..minetest.pos_to_string(d.target_pos)..".")
+ return false -- přemístění selhalo
+ end
+ end
+
+ -- načíst cílovou oblast
+ minetest.load_area(vector.round(d.target_pos))
+
+ -- přemístit postavu
+ player:set_pos(d.target_pos)
+
+ -- zrušit rychlost
+ if d.set_velocity ~= false then
+ player:add_velocity(vector.multiply(player:get_velocity(), -1.0))
+ end
+
+ -- nastavit natočení
+ if d.look_horizontal ~= nil then
+ player:set_look_horizontal(d.look_horizontal)
+ end
+ if d.look_vertical ~= nil then
+ player:set_look_vertical(d.look_vertical)
+ end
+
+ -- zavolat funkci po přemístění
+ if d.callback_after ~= nil then
+ d.callback_after()
+ end
+
+ -- přehrát zvuk po přemístění
+ if d.sound_after ~= nil then
+ minetest.sound_play(d.sound_after, {pos = d.target_pos, max_hear_distance = d.sound_after_max_hearing_distance or 50.0})
+ end
+
+ if d.type == "player" then
+ ch_core.systemovy_kanal(player_name, "Přemístění úspěšné")
+ end
+ minetest.log("action", player_name.." teleported to "..minetest.pos_to_string(d.target_pos)..".")
+
+ return true
+ end
+end
+
+
+
+
+
+-- PUBLIC FUNCTIONS
+function ch_core.register_can_teleport(name, func)
+ -- func = function(player, online_charinfo, priority)
+ -- priority: 0 = very low priority, 1 = low priority, 2 = normal priority (player/normal), 3 = high priority (force), 4 = very high priority (admin)
+ -- should return: true or nil (can teleport), false (can't teleport)
+ if not name then
+ error("Name is required!")
+ end
+ can_teleport_callbacks[name] = func
+end
+
+--[[
+ d = {
+ type = string, // musí být jedno z:
+ // "admin": přemístění s nejvyšší prioritou, umí odpojovat od všech typů objektů a může k němu docházet i ve výkonu trestu
+ // "force": neinteraktivní přemístění s vyšší prioritou, umí odpojovat od objektů
+ // "player": interaktivní přemístění s normální prioritou, neumí odpojovat od objektů, vypisuje zprávy do četu
+ // "normal": neinteraktivní přemístění s normální prioritou, neumí odpojovat od objektů, nevypisuje zprávy
+ player = ObjRef or string, // postava k přemístění (objekt nebo jméno)
+ target_pos = vector, // cílová pozice
+
+ delay = float or nil, // zpoždění; během zpoždění může postava přemístění zrušit např. pohybem
+ look_horizontal = float or nil,
+ look_vertical = float or nil,
+ sound_before = SimpleSoundSpec or nil, // zvuk k přehrání na výchozí pozici těsně před přemístěním,
+ sound_before_max_hearing_distance = float or nil,
+ sound_after = SimpleSoundSpec or nil, // zvuk k přehrání na cílové pozici těsně po přemístění,
+ sound_after_max_hearing_distance = float or nil,
+ callback_before = function or nil, // funkce k zavolání těsně před přemístěním
+ callback_after = function or nil, // funkce k zavolání těsně po přemístění
+ // nebude zavolána, pokud bude přemístění přerušeno
+ close_formspec = bool or nil, // je-li true, v momentě přemístění uzavře klientovi formspec (nedoporučuje se)
+ set_velocity = bool or nil, // je-li true nebo nil, po přemístění nastaví postavě nulovou rychlost
+ }
+]]
+
+function ch_core.teleport_player(def)
+ local d = table.copy(def)
+ if d.type == nil or d.player == nil or d.target_pos == nil then
+ error("ch_core.teleport_player(): a required argument is missing: "..dump2(def))
+ end
+ if d.type ~= "admin" and d.type ~= "force" and d.type ~= "player" and d.type ~= "normal" then
+ error("ch_core.teleport_player(): unsupported teleport type '"..d.type.."'!")
+ end
+ d.target_pos = vector.copy(d.target_pos)
+
+ -- je postava ve hře?
+ local pinfo = ch_core.normalize_player(d.player)
+ if pinfo.role == "none" then
+ return false, "postava neexistuje"
+ end
+ local player = pinfo.player
+ local player_name = pinfo.player_name
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if player == nil or online_charinfo == nil then
+ return false, "postava není ve hře"
+ end
+ -- local player_pos = player:get_pos()
+
+ -- mohu přemístit postavu?
+ if d.type ~= "admin" and ch_core.je_ve_vykonu_trestu ~= nil then
+ local trest = ch_core.je_ve_vykonu_trestu(player_name)
+ if trest ~= nil then
+ if d.type == "player" then
+ ch_core.systemovy_kanal(player_name, "Jste ve výkonu trestu odnětí svobody! Zbývající výše trestu: "..trest)
+ end
+ return false, "postava je ve výkonu trestu"
+ end
+ end
+
+ -- zrušit stávající přemístění, je-li naplánováno
+ local timer_def = ch_core.get_ch_timer_info(online_charinfo, "teleportace")
+ if timer_def then
+ ch_core.cancel_teleport(player_name, false)
+ end
+
+ -- přemístit se zpožděním?
+ local delay = d.delay
+ if delay ~= nil and delay > 0.0 then
+ timer_def = {
+ label = "přemístění",
+ func = get_finish_teleport_func(player_name, d, false),
+ hudbar_icon = (minetest.registered_items["basic_materials:energy_crystal_simple"] or {}).inventory_image,
+ hudbar_bar = "hudbars_bar_timer.png^[multiply:#0000ff",
+
+ teleport_type = d.type,
+ start_pos = player and player:get_pos(),
+ target_pos = d.target_pos,
+ def = d,
+ }
+ if ch_core.start_ch_timer(online_charinfo, "teleportace", d.delay, timer_def) then
+ if d.type == "player" then
+ local delay_str = string.format("%.3f", delay)
+ delay_str = delay_str:gsub("%.", ",")
+ ch_core.systemovy_kanal(player_name, "Přemístění zahájeno (čas na přípravu: "..delay_str.." sekund).")
+ ch_core.set_temporary_titul(player_name, "přemísťuje se", true)
+ end
+ minetest.log("action", "Teleport of "..player_name.." to "..minetest.pos_to_string(d.target_pos).." started.")
+ player:set_properties{pointable = false}
+ return true
+ end
+ return false, "neznámý důvod"
+ end
+
+ -- přemístit okamžitě?
+ local f = get_finish_teleport_func(player_name, d, true)
+ return f()
+end
+
+--[[
+ Zjistí, zda je postava přemísťována.
+]]
+function ch_core.is_teleporting(player_name)
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo == nil then
+ return false
+ end
+ local timer_def = ch_core.get_ch_timer_info(online_charinfo, "teleportace")
+ if timer_def == nil then
+ return false
+ end
+ return timer_def.teleport_type
+end
+
+--[[
+ Zruší probíhající přemístění.
+]]
+function ch_core.cancel_teleport(player_name, due_to_player_action)
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo == nil then
+ return false
+ end
+ local timer_def = ch_core.get_ch_timer_info(online_charinfo, "teleportace")
+ if timer_def == nil then
+ return false
+ end
+ ch_core.cancel_ch_timer(online_charinfo, "teleportace")
+ ch_core.set_temporary_titul(player_name, "přemísťuje se", false)
+ local player = minetest.get_player_by_name(player_name)
+ if player ~= nil then
+ player:set_properties{pointable = true}
+ end
+ if timer_def.def.type == "player" then
+ ch_core.systemovy_kanal(player_name, ifthenelse(due_to_player_action,
+ "Přemístění zrušeno v důsledku akce hráče/ky nebo postavy.",
+ "Přemístění zrušeno."))
+ end
+ return true
+end
+
+--[[
+ Předčasně dokončí probíhající přemístění.
+]]
+function ch_core.finish_teleport(player_name)
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo == nil then
+ return false
+ end
+ local timer_def = ch_core.get_ch_timer_info(online_charinfo, "teleportace")
+ if timer_def ~= nil then
+ ch_core.cancel_ch_timer(online_charinfo, "teleportace")
+ ch_core.set_temporary_titul(player_name, "přemísťuje se", false)
+ return timer_def.func()
+ else
+ return false
+ end
+end
+
+-- /doma
+local function doma(player_name, pos)
+ if not pos then
+ return false, "Chybná pozice!"
+ end
+ local offline_charinfo = ch_data.get_offline_charinfo(player_name)
+ offline_charinfo.domov = minetest.pos_to_string(pos)
+ if not offline_charinfo.domov then
+ return false, "Pozice nebyla uložena!"
+ end
+ ch_data.save_offline_charinfo(player_name)
+ return true
+end
+
+-- /domů
+local function domu(player_name)
+ local offline_charinfo = ch_data.offline_charinfo[player_name]
+ if not offline_charinfo then
+ return false, "Interní údaje nebyly nalezeny!"
+ elseif not offline_charinfo.domov then
+ return false, "Nejprve si musíte nastavit domovskou pozici příkazem /doma."
+ end
+ local new_pos = minetest.string_to_pos(offline_charinfo.domov)
+ if not new_pos then
+ return false, "Uložená pozice má neplatný formát!"
+ end
+ ch_core.teleport_player{
+ type = "player",
+ player = player_name,
+ target_pos = new_pos,
+ delay = 30.0,
+ sound_before = ch_core.default_teleport_sound,
+ sound_after = ch_core.default_teleport_sound,
+ }
+ return true
+end
+
+-- /stavím
+local function stavim(player_name, pos)
+ if not pos then
+ return false, "Chybná pozice!"
+ end
+ local filter_result = {}
+ ch_core.stavby_get_all(function(record)
+ local distance = vector.distance(pos, record.pos)
+ if record.spravuje == player_name then
+ -- spravovaná stavba: max. vzdálenost 100 m, stav jakýkoliv kromě opuštěného
+ if distance <= 100 and record.stav ~= "opusteno" and record.stav ~= "k_smazani" then
+ filter_result[1] = record
+ end
+ else
+ -- nespravovaná stavba: max. vzdálenost 20 m, stav rozestaveno nebo rekonstrukce
+ if distance <= 20 and (record.stav == "rozestaveno" or record.stav == "rekonstrukce") then
+ filter_result[1] = record
+ end
+ end
+ end)
+ if filter_result[1] == nil then
+ return false, "Pro nastavení pozice příkazem /stavím musíte mít do 100 metrů vámi spravovanou stavbu, která není opuštěná nebo ke smazání, "..
+ "nebo musí být do 20 metrů cizí stavba, která je rozestavěná nebo v rekonstrukci."
+ end
+ local cas = ch_time.aktualni_cas()
+ local dnes = cas:YYYY_MM_DD()
+ local offline_charinfo = ch_data.get_offline_charinfo(player_name)
+ if not offline_charinfo then
+ return false, "Interní údaje nebyly nalezeny!"
+ end
+ local _old_pos, pos_date
+ if offline_charinfo.stavba ~= nil then
+ _old_pos, pos_date = offline_charinfo.stavba:match("^([^@]+)@([^@]+)$")
+ end
+ if pos_date == dnes then
+ return false, "Cílovou pozici příkazem /stavím lze nastavit jen jednou denně!"
+ end
+ offline_charinfo.stavba = core.pos_to_string(pos).."@"..dnes
+ ch_data.save_offline_charinfo(player_name)
+ core.log("action", player_name.." set their 'stavba' position to "..offline_charinfo.stavba)
+ return true, "Pozice pro příkaz /nastavbu nastavena. Pamatujte, že tuto pozici můžete změnit jen jednou denně."
+end
+
+-- /nastavbu
+local function nastavbu(player_name)
+ local offline_charinfo = ch_data.offline_charinfo[player_name]
+ if not offline_charinfo then
+ return false, "Interní údaje nebyly nalezeny!"
+ elseif not offline_charinfo.stavba or offline_charinfo.stavba == "" then
+ return false, "Nejprve si musíte nastavit pozici stavby příkazem /stavím."
+ end
+ local new_pos, pos_date = offline_charinfo.stavba:match("^([^@]+)@([^@]+)$")
+ new_pos = new_pos and core.string_to_pos(new_pos)
+ if not new_pos or not pos_date then
+ return false, "Uložená pozice má neplatný formát!"
+ end
+ ch_core.teleport_player{
+ type = "player",
+ player = player_name,
+ target_pos = new_pos,
+ delay = 20.0,
+ sound_before = ch_core.default_teleport_sound,
+ sound_after = ch_core.default_teleport_sound,
+ }
+ return true
+end
+
+-- /začátek
+local function zacatek(player_name, _param)
+ local offline_charinfo = ch_data.offline_charinfo[player_name]
+ if not offline_charinfo then
+ return false, "Interní údaje nebyly nalezeny!"
+ end
+ local player = minetest.get_player_by_name(player_name)
+ local player_pos = player and player:get_pos()
+ if not player_pos then
+ return false
+ end
+ local i = tonumber(offline_charinfo.zacatek_kam) or 1
+ local zacatek_pos = ch_core.positions["zacatek_"..i] or ch_core.positions["zacatek_1"] or vector.zero()
+ if vector.distance(player_pos, zacatek_pos) < 5 then
+ return false, "Jste příliš blízko počáteční pozice!"
+ end
+ ch_core.teleport_player{
+ type = "player",
+ player = player_name,
+ target_pos = zacatek_pos,
+ delay = 30.0,
+ sound_before = ch_core.default_teleport_sound,
+ sound_after = ch_core.default_teleport_sound,
+ }
+ return true
+end
+
+local function bn_nastavit(player)
+ local player_name = player:get_player_name()
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo == nil then
+ return false, "Interní údaje nebyly nalezeny!"
+ end
+ local player_pos = player:get_pos()
+ online_charinfo.bod_navratu = core.pos_to_string(player_pos).."|"..core.get_us_time()
+ return true, "Bod návratu úspěšně nastaven na: "..core.pos_to_string(vector.round(player_pos))
+end
+
+local function bn_zpet(player)
+ local player_name = player:get_player_name()
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo == nil then
+ return false, "Interní údaje nebyly nalezeny!"
+ end
+ local s = online_charinfo.bod_navratu or ""
+ local target_pos, set_ts = s:match("^([^|]+)|([^|]+)$")
+ if target_pos ~= nil then
+ target_pos = core.string_to_pos(target_pos)
+ end
+ if set_ts ~= nil then
+ set_ts = tonumber(set_ts)
+ end
+ if target_pos == nil or set_ts == nil then
+ return false, "V poslední době jste si nenastavili bod návratu."
+ end
+ local now = core.get_us_time()
+ local diff = (now - set_ts) * 1.0e-6
+ if diff > 30 * 60 then
+ online_charinfo.bod_navratu = ""
+ return false, "V poslední době jste si nenastavili bod návratu."
+ elseif diff < 6 then
+ diff = 0
+ end
+ ch_core.teleport_player{
+ type = "player",
+ player = player_name,
+ target_pos = target_pos,
+ delay = math.max(1, diff / 60),
+ sound_before = ch_core.default_teleport_sound,
+ sound_after = ch_core.default_teleport_sound,
+ }
+ return true
+end
+
+-- exportovat funkce pro ch_extras:
+ch_core.bn_nastavit = bn_nastavit
+ch_core.bn_zpet = bn_zpet
+
+local def
+
+-- /začátek
+def = {
+ description = "Přenese vás na počáteční pozici.",
+ func = zacatek,
+}
+core.register_chatcommand("zacatek", def);
+core.register_chatcommand("začátek", def);
+core.register_chatcommand("zacatek", def);
+core.register_chatcommand("yačátek", def);
+core.register_chatcommand("yacatek", def);
+
+-- /doma
+def = {
+ description = "Uloží domovskou pozici pro pozdější návrat příkazem /domů.",
+ privs = {home = true},
+ func = function(player_name)
+ local player = minetest.get_player_by_name(player_name)
+ local pos = player and player:get_pos()
+ if not pos then
+ return false
+ else
+ local result, err = doma(player_name, pos)
+ if result then
+ return result, "Domovská pozice nastavena!"
+ else
+ return false, err
+ end
+ end
+ end
+}
+core.register_chatcommand("doma", table.copy(def));
+
+-- /domů
+def = {
+ description = "Přenese vás na domovskou pozici uloženou příkazem /doma.",
+ privs = {home = true},
+ func = domu,
+}
+core.register_chatcommand("domů", def);
+core.register_chatcommand("domu", def);
+
+-- /stavím
+def = {
+ description = "Uloží pozici na stavbě pro pozdější návrat příkazem /nastavbu. Pozici lze změnit jen jednou denně.",
+ privs = {home = true},
+ func = function(player_name)
+ local player = minetest.get_player_by_name(player_name)
+ local pos = player and player:get_pos()
+ if pos then
+ return stavim(player_name, pos)
+ else
+ return false
+ end
+ end
+}
+core.register_chatcommand("stavím", def)
+core.register_chatcommand("stavim", def)
+
+-- /nastavbu
+def = {
+ description = "Přenese vás na pozici na stavbě uloženou příkazem /stavím.",
+ privs = {home = true},
+ func = nastavbu,
+}
+core.register_chatcommand("nastavbu", def)
+core.register_chatcommand("na_stavbu", def)
+
+-- /bodnávratu
+def = {
+ description = "Uloží vaši pozici jako bod návratu pro příkaz /zpět.",
+ privs = {home = true},
+ func = function(player_name, param)
+ local player = core.get_player_by_name(player_name)
+ if player ~= nil then
+ return bn_nastavit(player)
+ end
+ end,
+}
+core.register_chatcommand("bn", def)
+core.register_chatcommand("bodnávratu", def)
+core.register_chatcommand("bodnavratu", def)
+
+-- /zpět
+def = {
+ description = "Přenese vás na místo, které jste si v poslední půlhodině uložili příkazem /bodnávratu.",
+ privs = {home = true},
+ func = function(player_name, param)
+ local player = core.get_player_by_name(player_name)
+ if player ~= nil then
+ return bn_zpet(player)
+ end
+ end,
+}
+core.register_chatcommand("zpět", def)
+core.register_chatcommand("zpet", def)
+
+ch_core.close_submod("teleportace")