123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- --[[
- Advanced Portals (advanced_portals), A minetest add-on that allows certain users to create and place portals on their minetest world.
- Copyright (c) 2020 Genshin <emperor_genshin@hotmail.com>
- License: GPLv3
- --]]
- --Delay to when handlers update
- local update_interval = 25
- local particle_emitter_interval = 4
- --Delay to when ambiance will loop
- local ambiance_interval = 200
- local ambiance_list = {}
- local debug = minetest.settings:get_bool("enable_portals_debug") or false
- local function get_count_from_table(table)
- count = 0
- for k,v in pairs(table) do
- count = count + 1
- end
- return count
- end
- local function do_particle_effect(pos, delay, amount, texture, min_size, max_size, radius, gravity, glow)
- radius = radius or 2
- min_size = min_size or 0.5
- max_size = max_size or 1
- gravity = gravity or -10
- glow = glow or 0
- delay = delay or 0.25
- minetest.add_particlespawner({
- amount = amount,
- time = delay,
- minpos = pos,
- maxpos = pos,
- minvel = {x = -radius, y = -radius, z = -radius},
- maxvel = {x = radius, y = radius, z = radius},
- minacc = {x = 0, y = gravity, z = 0},
- maxacc = {x = 0, y = gravity, z = 0},
- minexptime = 0.1,
- maxexptime = 1,
- minsize = min_size,
- maxsize = max_size,
- texture = texture,
- glow = glow,
- })
- end
- local function update_ambiance_to_players_within_radius(self, distance)
- local pos = self.object:get_pos()
- local node = self.portal_nodename
- local ambiance_sound = get_portal_handler_sounds(node, "portal_ambiance")
- local ambiance_distance = get_portal_handler_sounds(node, "sound_distance")
- local entities = minetest.get_objects_inside_radius(pos, ambiance_distance)
- local players = minetest.get_connected_players()
- local within_radius = {}
- --Get the following players within the radius and stack them in a table
- for k,v in pairs(entities) do
- local entity = v
- if not entity then
- within_radius = {}
- break
- end
- if entity:is_player() then
- local name = entity:get_player_name()
- within_radius[name] = entity
- end
- end
- --Check which players are ouside of radius
- for _,player in pairs(players) do
- local name = player:get_player_name()
- if within_radius[name] then
- if not ambiance_list[name] then
- local ambiance = minetest.sound_play(ambiance_sound, {
- object = player,
- loop = true,
- })
- ambiance_list[name] = ambiance
- end
- else
- if ambiance_list[name] then
- minetest.sound_stop(ambiance_list[name])
- ambiance_list[name] = nil
- end
- end
- end
- end
- local function force_stop_ambiance_to_players()
- local players = minetest.get_connected_players()
- --Check which players are ouside of radius
- for _,player in pairs(players) do
- local name = player:get_player_name()
- if name then
- if ambiance_list[name] then
- minetest.sound_stop(ambiance_list[name])
- ambiance_list[name] = nil
- end
- end
- end
- end
- --[Local Function] Teleport Player or Mob to a location or pparticle_emitter_intervalortal
- local function teleport_entity(userdata, self)
- local portal_data = get_portal_data(self.portal)
- local linked = portal_data.linked
- local linked_portal_data = nil
- local pos = nil
- local height = nil
- local node = self.portal_nodename
- local warp_sound = get_portal_handler_sounds(node, "portal_warp") or ""
- local sound_distance = get_portal_handler_sounds(node, "sound_distance")
- local entity_pos = userdata:get_pos()
- if linked == true then
- linked_portal_data = get_portal_data(portal_data.linked_portal)
- if not linked_portal_data then
- return
- end
- pos = linked_portal_data.location
- --height = get_portal_handler_origin_height(self.portal_nodename, "warp_handler")
- if pos == nil then
- minetest.log("error", "Failed to find location of linked portal. Refusing to teleport entity to a unknown location")
- return
- end
-
- if debug == true then
- minetest.chat_send_all("Teleported Something! - Linked")
- end
-
- do_particle_effect(entity_pos, 0.25, 1, "warp_particle.png", 10, 20, radius, 20, 30)
- userdata:set_pos({x = pos.x, y = pos.y + 3, z = pos.z})
- elseif linked == false then
- pos = portal_data.coordinates
- if pos == nil then
- minetest.log("error", "Failed to find specified destination. Refusing to teleport entity to a unknown location")
- return
- end
- if debug == true then
- minetest.chat_send_all("Teleported Something! - Not Linked")
- end
- do_particle_effect(entity_pos, 5, 25, "warp_particle.png", math.random(5,10), math.random(10,20), radius, 20, 10)
- userdata:set_pos(pos)
- end
- if userdata:is_player() then
-
- local name = userdata:get_player_name()
- if ambiance_list[name] then
- minetest.sound_stop(ambiance_list[name])
- ambiance_list[name] = nil
- end
- end
- minetest.sound_play(warp_sound, {
- pos = self.object:get_pos(),
- max_hear_distance = sound_distance,
- gain = 10.0,
- })
- minetest.sound_play(warp_sound, {
- pos = pos,
- max_hear_distance = sound_distance,
- gain = 10.0,
- })
- return
- end
- --[Local Function] Get node from vertical position
- local function get_node_from_vertical_pos(userdata, direction, vertical_height)
- local pos = userdata:get_pos()
- if direction == nil then else
- if direction == "up" then
- pos = {x = pos.x, y = pos.y + vertical_height, z = pos.z}
- elseif direction == "down" then
- pos = {x = pos.x, y = pos.y - vertical_height, z = pos.z}
- end
- end
- local result = tostring(minetest.get_node(pos)["name"]) or "Error"
- return result
- end
- --[Local Function] Portal Warp Handler Behavior
- local function handler_step(self)
- self.update_timer = self.update_timer + 1
- if self.emit_particles == "true" then
- self.particle_emitter_timer = self.particle_emitter_timer + 1
- if self.particle_emitter_timer >= particle_emitter_interval then
- local texture = get_portal_particle_values(self.portal_nodename, "texture")
- local delay = get_portal_particle_values(self.portal_nodename, "delay")
- local spread = get_portal_particle_values(self.portal_nodename, "spread")
- local pos = self.object:get_pos()
- do_particle_effect(pos, delay, 1, texture, math.random(5,8), math.random(8,12), spread, 1, 30)
- self.particle_emitter_timer = 0
- end
- end
- if self.update_timer >= update_interval then
- if self.origin_height == nil then
- force_stop_ambiance_to_players()
- self.object:remove()
- return
- end
- local portal_node = tostring(get_node_from_vertical_pos(self.object, "down", self.origin_height))
- --minetest.chat_send_all(tostring(portal_node)..", "..tostring(self.portal_node)..", "..tostring(self.origin_height)..", "..tostring(self.portal))
- if self.portal == nil or self.portal_node == nil then --delete yourself if it's not associated with a portal (failsafe)
- force_stop_ambiance_to_players()
- --minetest.chat_send_all("Uh Oh 2 - Handler")
- self.object:remove()
- return
- elseif portal_node ~= self.portal_node then --No Portal below you?, if so then kill yourself (failsafe)
- force_stop_ambiance_to_players()
- --minetest.chat_send_all("Uh Oh 3 - Handler")
- self.object:remove()
- return
- end
- update_ambiance_to_players_within_radius(self)
- local portal_data = get_portal_data(self.portal)
-
- --If data hasn't loaded successfully, skip the rest of the stuff (failsafe)
- if portal_data == nil then
- return
- end
- self.emit_particles = get_portal_particle_values(self.portal_nodename, "flag") or self.emit_particles
- local pos = self.object:get_pos()
- local detected_entities = minetest.get_objects_inside_radius(pos, self.scan_radius)
- local private = portal_data.private
- if private == true then
- local whitelist = portal_data.whitelist
- for _,entity in pairs(detected_entities) do
- if entity:is_player() then
- if debug == true then
- minetest.chat_send_all("Attempting to teleport someone! - Private")
- end
- local player = entity
- local player_name = player:get_player_name()
- for k,v in pairs(whitelist) do
- local allowed = v
- local pname = player:get_player_name()
- if pname == allowed or player_name == self.portal_owner then
- teleport_entity(player, self)
- end
- end
- else
- local npc = entity:get_luaentity()
- if npc.type and npc ~= self then
- if debug == true then
- minetest.chat_send_all("Attempting to teleport something! - Private")
- end
- teleport_entity(npc.object, self)
- end
- end
- end
- elseif private == false then
- for _,entity in pairs(detected_entities) do
- if entity:is_player() then
- if debug == true then
- minetest.chat_send_all("Attempting to teleport someone! - Not Private")
- end
- teleport_entity(entity, self)
- else
- local npc = entity:get_luaentity()
- if npc.type and npc ~= self then
- if debug == true then
- minetest.chat_send_all("Attempting to teleport something! - Not Private")
- end
- teleport_entity(npc.object, self)
- end
- end
- end
- end
- ::portal_handler_loop_end::
- self.update_timer = 0
- end
- end
- local function update_portal_nametag(self)
- local portal_data = get_portal_data(self.portal)
-
- --If data hasn't loaded successfully, skip the rest of the stuff (failsafe)
- if portal_data == nil then
- return
- end
- self.object:set_properties({
- nametag = minetest.colorize("#0066ff", portal_data.nametag).."\n"..minetest.colorize("#959595", "["..portal_data.portal_name.."]")
- })
- end
- --[Local Function] Portal Nametag Behavior
- local function nametag_step(self)
- self.update_timer = self.update_timer + 1
- if self.update_timer >= update_interval then
- if self.origin_height == nil then
- --minetest.chat_send_all("Uh Oh 1 - Nametag")
- self.object:remove()
- return
- end
- local portal_node = tostring(get_node_from_vertical_pos(self.object, "down", self.origin_height))
- --minetest.chat_send_all(tostring(portal_node)..", "..tostring(self.portal_node)..", "..tostring(self.origin_height)..", "..tostring(self.portal))
- if self.portal == nil or self.portal_node == nil then --delete yourself if it's not associated with a portal (failsafe)
- --minetest.chat_send_all("Uh Oh 2 - Nametag")
- self.object:remove()
- return
- elseif portal_node ~= self.portal_node then --No Portal below you?, if so then kill yourself (failsafe)
- --minetest.chat_send_all("Uh Oh 3 - Nametag")
- self.object:remove()
- return
- end
- local portal_data = get_portal_data(self.portal)
-
- --If data hasn't loaded successfully, skip the rest of the stuff (failsafe)
- if portal_data == nil then
- return
- end
- update_portal_nametag(self)
- self.update_timer = 0
- end
- end
- --[Entity] Portal Teleportation Handler
- minetest.register_entity("advanced_portals:portal_handler", {
- physical = false,
- collisionbox = {0, 0, 0, 0, 0, 0},
- visual = "sprite",
- glow = 30,
- textures = {"portal_deactivated.png"},
- visual_size = {x = 0, y = 0},
- origin_height = nil,
- portal = nil,
- portal_node = nil,
- portal_nodename = nil,
- scan_radius = 2,
- update_timer = 0,
- particle_emitter_timer = 0,
- emit_particles = "false",
- particle_texture = "",
- particle_delay = 0,
- particle_spread = 0,
- on_activate = function(self, staticdata, dtime_s)
- -- load entity variables
- local tmp = minetest.deserialize(staticdata)
- if tmp then
- for _,stat in pairs(tmp) do
- self[_] = stat
- end
- end
- self.set = self.set
- self.radius = self.radius
- self.object:set_armor_groups({immortal = 100}) --Can't die by anything
- self.object:set_properties(self)
- if debug == true then
- self.object:set_properties({
- nametag = "<[DEBUG]: Portal Handler Position>"
- })
- end
- if self.portal == nil then else
- local portal_data = get_portal_data(self.portal)
- local current_origin_height = get_portal_handler_origin_height(self.portal_nodename, "warp_handler")
- --print("[DEBUG] warp handler current_origin_height = "..current_origin_height)
- if current_origin_height ~= self.origin_height then
- local pos = portal_data.location
- self.origin_height = current_origin_height
- self.object:set_pos({x = pos.x, y = pos.y + current_origin_height, z = pos.z})
- end
-
- end
- end,
- get_staticdata = function(self)
- local tmp = {}
- for _,stat in pairs(self) do
- local t = type(stat)
- if t ~= 'function'
- and t ~= 'nil'
- and t ~= 'userdata' then
- tmp[_] = self[_]
- end
- end
- return minetest.serialize(tmp)
- end,
- on_punch = function(self, hitter) --Can't get punched by anything
- return
- end,
- on_step = function(self)
- handler_step(self)
- end
- })
- --[Entity] Portal Nametag
- minetest.register_entity("advanced_portals:portal_nametag", {
- physical = false,
- collisionbox = {0, 0, 0, 0, 0, 0},
- visual = "sprite",
- tag = "",
- glow = 30,
- textures = {"portal_deactivated.png"},
- visual_size = {x = 0, y = 0},
- origin_height = nil,
- portal = nil,
- set = false,
- portal_node = nil,
- portal_nodename = nil,
- scan_radius = 2,
- update_timer = 0,
- on_activate = function(self, staticdata, dtime_s)
- -- load entity variables
- local tmp = minetest.deserialize(staticdata)
-
- if tmp then
- for _,stat in pairs(tmp) do
- self[_] = stat
- end
- end
- self.radius = self.radius
- self.object:set_armor_groups({immortal = 100}) --Can't die by anything
- self.object:set_properties(self)
- update_portal_nametag(self)
- if self.portal == nil then else
- local portal_data = get_portal_data(self.portal)
- local current_origin_height = get_portal_handler_origin_height(self.portal_nodename, "nametag")
- --print("[DEBUG] nametag current_origin_height = "..current_origin_height)
- if current_origin_height ~= self.origin_height then
- local pos = portal_data.location
- self.origin_height = current_origin_height
- self.object:set_pos({x = pos.x, y = pos.y + current_origin_height, z = pos.z})
- end
- end
- end,
- get_staticdata = function(self)
- local tmp = {}
- for _,stat in pairs(self) do
- local t = type(stat)
- if t ~= 'function'
- and t ~= 'nil'
- and t ~= 'userdata' then
- tmp[_] = self[_]
- end
- end
- return minetest.serialize(tmp)
- end,
- on_punch = function(self, hitter) --Can't get punched by anything
- return
- end,
- on_step = function(self)
- nametag_step(self)
- end,
- })
|