diff --git a/teleport_potion/depends.txt b/teleport_potion/depends.txt new file mode 100644 index 0000000..8a8d2a2 --- /dev/null +++ b/teleport_potion/depends.txt @@ -0,0 +1,3 @@ +default +intllib? +lucky_block? \ No newline at end of file diff --git a/teleport_potion/init.lua b/teleport_potion/init.lua new file mode 100644 index 0000000..ce4ef93 --- /dev/null +++ b/teleport_potion/init.lua @@ -0,0 +1,503 @@ + +--= Teleport Potion mod by TenPlus1 + +-- Create teleport potion or pad, place then right-click to enter coords +-- and step onto pad or walk into the blue portal light, portal closes after +-- 10 seconds, pad remains, potions are throwable... SFX are license Free... + +-- Load support for intllib. +local MP = minetest.get_modpath(minetest.get_current_modname()) +local S, NS = dofile(MP.."/intllib.lua") + + +-- max teleport distance +local dist = tonumber(minetest.settings:get("map_generation_limit") or 31000) + +-- creative check +local creative_mode_cache = minetest.settings:get_bool("creative_mode") +local function is_creative(name) + return creative_mode_cache or minetest.check_player_privs(name, {creative = true}) +end + +local check_coordinates = function(str) + + if not str or str == "" then + return nil + end + + -- get coords from string + local x, y, z = string.match(str, "^(-?%d+),(-?%d+),(-?%d+)$") + + -- check coords + if x == nil or string.len(x) > 6 + or y == nil or string.len(y) > 6 + or z == nil or string.len(z) > 6 then + return nil + end + + -- convert string coords to numbers + x = tonumber(x) + y = tonumber(y) + z = tonumber(z) + + -- are coords in map range ? + if x > dist or x < -dist + or y > dist or y < -dist + or z > dist or z < -dist then + return nil + end + + -- return ok coords + return {x = x, y = y, z = z} +end + + +-- particle effects +local function tp_effect(pos) + minetest.add_particlespawner({ + amount = 20, + time = 0.25, + minpos = pos, + maxpos = pos, + minvel = {x = -2, y = 1, z = -2}, + maxvel = {x = 2, y = 2, z = 2}, + minacc = {x = 0, y = -2, z = 0}, + maxacc = {x = 0, y = -4, z = 0}, + minexptime = 0.1, + maxexptime = 1, + minsize = 0.5, + maxsize = 1.5, + texture = "particle.png", + glow = 15, + }) +end + +local teleport_destinations = {} + +local function set_teleport_destination(playername, dest) + teleport_destinations[playername] = dest + tp_effect(dest) + minetest.sound_play("portal_open", { + pos = dest, + gain = 1.0, + max_hear_distance = 10 + }) +end + +-------------------------------------------------------------------------------- +--- Teleport portal +-------------------------------------------------------------------------------- +minetest.register_node("teleport_potion:portal", { + drawtype = "plantlike", + tiles = { + {name="portal.png", + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 1.0 + } + } + }, + light_source = 13, + walkable = false, + paramtype = "light", + pointable = false, + buildable_to = true, + waving = 1, + sunlight_propagates = true, + damage_per_second = 1, -- walking into portal hurts player + + -- start timer when portal appears + on_construct = function(pos) + minetest.get_node_timer(pos):start(10) + end, + + -- remove portal after 10 seconds + on_timer = function(pos) + + minetest.sound_play("portal_close", { + pos = pos, + gain = 1.0, + max_hear_distance = 10 + }) + + minetest.remove_node(pos) + end, + on_blast = function() end, + drop = {}, +}) + + +-- Throwable potion +local function throw_potion(itemstack, player) + + local playerpos = player:get_pos() + + local obj = minetest.add_entity({ + x = playerpos.x, + y = playerpos.y + 1.5, + z = playerpos.z + }, "teleport_potion:potion_entity") + + local dir = player:get_look_dir() + local velocity = 20 + + obj:set_velocity({ + x = dir.x * velocity, + y = dir.y * velocity, + z = dir.z * velocity + }) + + obj:set_acceleration({ + x = dir.x * -3, + y = -9.5, + z = dir.z * -3 + }) + + obj:set_yaw(player:get_look_horizontal()) + obj:get_luaentity().player = player +end + + +local potion_entity = { + physical = true, + visual = "sprite", + visual_size = {x = 1.0, y = 1.0}, + textures = {"potion.png"}, + collisionbox = {0,0,0,0,0,0}, + lastpos = {}, + player = "", +} + +potion_entity.on_step = function(self, dtime) + + if not self.player then + self.object:remove() + return + end + + local pos = self.object:get_pos() + + if self.lastpos.x ~= nil then + + local vel = self.object:get_velocity() + + -- only when potion hits something physical + if vel.x == 0 + or vel.y == 0 + or vel.z == 0 then + + if self.player ~= "" then + + -- round up coords to fix glitching through doors + self.lastpos = vector.round(self.lastpos) + + self.player:set_pos(self.lastpos) + + minetest.sound_play("portal_close", { + pos = self.lastpos, + gain = 1.0, + max_hear_distance = 5 + }) + + tp_effect(self.lastpos) + end + + self.object:remove() + + return + + end + end + + self.lastpos = pos +end + +minetest.register_entity("teleport_potion:potion_entity", potion_entity) + + +-------------------------------------------------------------------------------- +--- Teleport potion +-------------------------------------------------------------------------------- +minetest.register_node("teleport_potion:potion", { + tiles = {"pad.png"}, + drawtype = "signlike", + paramtype = "light", + paramtype2 = "wallmounted", + walkable = false, + sunlight_propagates = true, + description = S("Teleport Potion (use to set destination; place to open portal)"), + inventory_image = "potion.png", + wield_image = "potion.png", + groups = {dig_immediate = 3, vessel = 1}, + selection_box = {type = "wallmounted"}, + + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "node" then + set_teleport_destination(user:get_player_name(), pointed_thing.above) + else + throw_potion(itemstack, user) + if not is_creative(user:get_player_name()) then + itemstack:take_item() + return itemstack + end + end + end, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + local name = placer:get_player_name() + local dest = teleport_destinations[name] + if dest then + minetest.set_node(pos, {name = "teleport_potion:portal"}) + local meta = minetest.get_meta(pos) + -- Set portal destination + meta:set_int("x", dest.x) + meta:set_int("y", dest.y) + meta:set_int("z", dest.z) + -- Portal open effect and sound + tp_effect(pos) + minetest.sound_play("portal_open", { + pos = pos, + gain = 1.0, + max_hear_distance = 10 + }) + else + minetest.chat_send_player(name, S("Potion failed!")) + minetest.remove_node(pos) + minetest.add_item(pos, "teleport_potion:potion") + end + end, +}) + +-- teleport potion recipe +minetest.register_craft({ + output = "teleport_potion:potion", + recipe = { + {"", "default:diamond", ""}, + {"default:diamond", "vessels:glass_bottle", "default:diamond"}, + {"", "default:diamond", ""}, + }, +}) + +-------------------------------------------------------------------------------- +--- Teleport pad +-------------------------------------------------------------------------------- +local teleport_formspec_context = {} + +minetest.register_node("teleport_potion:pad", { + tiles = {"padd.png", "padd.png^[transformFY"}, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + legacy_wallmounted = true, + walkable = true, + sunlight_propagates = true, + description = S("Teleport Pad (use to set destination; place to open portal)"), + inventory_image = "padd.png", + wield_image = "padd.png", + light_source = 5, + groups = {snappy = 3}, + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5} + }, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5} + }, + + -- Save pointed nodes coordinates as destination for further portals + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "node" then + set_teleport_destination(user:get_player_name(), pointed_thing.above) + end + end, + + -- Initialize teleport to saved location or the current position + after_place_node = function(pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + local name = placer:get_player_name() + local dest = teleport_destinations[name] + if not dest then + dest = pos + end + -- Set coords + meta:set_int("x", dest.x) + meta:set_int("y", dest.y) + meta:set_int("z", dest.z) + + meta:set_string("infotext", S("Pad Active (@1,@2,@3)", + dest.x, dest.y, dest.z)) + + minetest.sound_play("portal_open", { + pos = pos, + gain = 1.0, + max_hear_distance = 10 + }) + end, + + -- Show formspec depending on the players privileges. + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + local name = clicker:get_player_name() + + if minetest.is_protected(pos, name) then + minetest.record_protection_violation(pos, name) + return + end + + local meta = minetest.get_meta(pos) + local coords = { + x = meta:get_int("x"), + y = meta:get_int("y"), + z = meta:get_int("z") + } + local coords = coords.x .. "," .. coords.y .. "," .. coords.z + local desc = meta:get_string("desc") + formspec = "field[desc;" .. S("Description") .. ";" + .. minetest.formspec_escape(desc) .. "]" + -- Only allow privileged players to change coordinates + if minetest.check_player_privs(name, "teleport") then + formspec = formspec .. + "field[coords;" .. S("Teleport coordinates") .. ";" .. coords .. "]" + end + + teleport_formspec_context[name] = { + pos = pos, + coords = coords, + desc = desc, + } + minetest.show_formspec(name, "teleport_potion:set_destination", formspec) + end, +}) + +-- Check and set coordinates +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "teleport_potion:set_destination" then + return false + end + local name = player:get_player_name() + local context = teleport_formspec_context[name] + if not context then return false end + teleport_formspec_context[name] = nil + local meta = minetest.get_meta(context.pos) + -- Coordinates were changed + if fields.coords and fields.coords ~= context.coords then + local coords = check_coordinates(fields.coords) + if coords then + meta:set_int("x", coords.x) + meta:set_int("y", coords.y) + meta:set_int("z", coords.z) + else + minetest.chat_send_player(name, S("Teleport Pad coordinates failed!")) + end + end + -- Update infotext + if fields.desc and fields.desc ~= "" then + meta:set_string("desc", fields.desc) + meta:set_string("infotext", S("Teleport to @1", fields.desc)) + else + local coords = minetest.string_to_pos("(" .. context.coords .. ")") + meta:set_string("infotext", S("Pad Active (@1,@2,@3)", + coords.x, coords.y, coords.z)) + end + return true +end) + +-- teleport pad recipe +minetest.register_craft({ + output = "teleport_potion:pad", + recipe = { + {"teleport_potion:potion", "default:glass", "teleport_potion:potion"}, + {"default:glass", "default:mese", "default:glass"}, + {"teleport_potion:potion", "default:glass", "teleport_potion:potion"} + } +}) + + +-- check portal & pad, teleport any entities on top +minetest.register_abm({ + label = "Potion/Pad teleportation", + nodenames = {"teleport_potion:portal", "teleport_potion:pad"}, + interval = 2, + chance = 1, + catch_up = false, + + action = function(pos, node, active_object_count, active_object_count_wider) + + -- check objects inside pad/portal + local objs = minetest.get_objects_inside_radius(pos, 1) + + if #objs == 0 then + return + end + + -- get coords from pad/portal + local meta = minetest.get_meta(pos) + + if not meta then return end -- errorcheck + + local target_coords = { + x = meta:get_int("x"), + y = meta:get_int("y"), + z = meta:get_int("z") + } + + for n = 1, #objs do + + if objs[n]:is_player() then + + -- play sound on portal end + minetest.sound_play("portal_close", { + pos = pos, + gain = 1.0, + max_hear_distance = 5 + }) + + -- move player + objs[n]:set_pos(target_coords) + + -- paricle effects on arrival + tp_effect(target_coords) + + -- play sound on destination end + minetest.sound_play("portal_close", { + pos = target_coords, + gain = 1.0, + max_hear_distance = 5 + }) + + -- rotate player to look in pad placement direction + local rot = node.param2 + local yaw = 0 + + if rot == 0 or rot == 20 then + yaw = 0 -- north + elseif rot == 2 or rot == 22 then + yaw = 3.14 -- south + elseif rot == 1 or rot == 23 then + yaw = 4.71 -- west + elseif rot == 3 or rot == 21 then + yaw = 1.57 -- east + end + + objs[n]:set_look_yaw(yaw) + end + end + end +}) + + +-- add lucky blocks + +-- Teleport Potion mod +if minetest.get_modpath("lucky_block") then + lucky_block:add_blocks({ + {"dro", {"teleport_potion:potion"}, 2}, + {"tel"}, + {"dro", {"teleport_potion:pad"}, 1}, + {"lig"}, + }) +end + +print ("[MOD] Teleport Potion loaded") diff --git a/teleport_potion/intllib.lua b/teleport_potion/intllib.lua new file mode 100644 index 0000000..6669d72 --- /dev/null +++ b/teleport_potion/intllib.lua @@ -0,0 +1,45 @@ + +-- Fallback functions for when `intllib` is not installed. +-- Code released under Unlicense . + +-- Get the latest version of this file at: +-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua + +local function format(str, ...) + local args = { ... } + local function repl(escape, open, num, close) + if escape == "" then + local replacement = tostring(args[tonumber(num)]) + if open == "" then + replacement = replacement..close + end + return replacement + else + return "@"..open..num..close + end + end + return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) +end + +local gettext, ngettext +if minetest.get_modpath("intllib") then + if intllib.make_gettext_pair then + -- New method using gettext. + gettext, ngettext = intllib.make_gettext_pair() + else + -- Old method using text files. + gettext = intllib.Getter() + end +end + +-- Fill in missing functions. + +gettext = gettext or function(msgid, ...) + return format(msgid, ...) +end + +ngettext = ngettext or function(msgid, msgid_plural, n, ...) + return format(n==1 and msgid or msgid_plural, ...) +end + +return gettext, ngettext