diff options
author | orwell <orwell@bleipb.de> | 2025-05-27 21:17:10 +0200 |
---|---|---|
committer | orwell <orwell@bleipb.de> | 2025-05-27 21:17:10 +0200 |
commit | 3470687be0af7254aca478ead1e9c72757edf070 (patch) | |
tree | 41781361c979cfda61a924d7471978037d68005e /ch_core/teleportace.lua | |
parent | 8506dd2825b715293138976a5ad1fa11a46206a7 (diff) | |
download | advtrains-cesky-hvozd.tar.gz advtrains-cesky-hvozd.tar.bz2 advtrains-cesky-hvozd.zip |
Add CH dependencies temporarily.cesky-hvozd
Before merge to master should remove them again and split out util functions (e.g. formspec lib)
Diffstat (limited to 'ch_core/teleportace.lua')
-rw-r--r-- | ch_core/teleportace.lua | 578 |
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") |