123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- preload_tp = preload_tp or {}
- preload_tp.modpath = minetest.get_modpath("preload_tp")
- -- Localize for performance.
- local vector_distance = vector.distance
- local vector_round = vector.round
- local math_floor = math.floor
- function preload_tp.finalize(parameters)
- local pname = parameters.player_name
- local force = parameters.force_teleport
- local pp = parameters.start_position
- local tp = parameters.target_position
- local pre_cb = parameters.pre_teleport_callback
- local post_cb = parameters.post_teleport_callback
- local cb_param = parameters.callback_param
- local tpsound = parameters.teleport_sound
- local pfx = parameters.particle_effects
-
- -- Find the player.
- local player = minetest.get_player_by_name(pname)
- if not player or not player:is_player() then
- -- The player left, or something. Do not teleport them.
- minetest.log("action", pname .. " left the game while a teleport callback was in progress")
- return
- end
- -- The player may optionally be force-teleported.
- if not force then
- -- Did the player move?
- if vector_distance(pp, player:get_pos()) > 1.5 then
- minetest.chat_send_player(pname, "# Server: Transport error. You cannot move while a transport is in progress.")
- return
- end
- -- If the player killed themselves, do not teleport them.
- if player:get_hp() == 0 then
- minetest.chat_send_player(pname, "# Server: Transport error. You are dead.")
- return
- end
- end
- -- But we must never teleport a player who is attached.
- if default.player_attached[pname] then
- minetest.chat_send_player(pname, "# Server: Transport error. Player attached!")
- return
- end
- -- Execute the callback function, if everything else succeeded.
- if pre_cb then
- -- If the pre-teleport callback returns 'success' then that
- -- signals that the teleport must be aborted for some reason.
- if pre_cb(cb_param) then
- minetest.chat_send_player(pname, "# Server: Transport canceled.")
- return
- end
- end
- minetest.log("action", "executing teleport callback for " .. pname .. "!")
- -- Teleport player only if they didn't move (or teleporting is forced).
- wield3d.on_teleport()
- rc.notify_realm_update(player, tp)
- player:set_pos(tp)
- minetest.log("action", pname .. " actually teleports to " .. minetest.pos_to_string(tp))
- -- Execute the callback function, if everything else succeeded.
- if post_cb then
- post_cb(cb_param)
- end
- local thesound = "teleport"
- if type(tpsound) == "string" then
- thesound = tpsound
- end
- -- The teleport sound, played @ old & new locations.
- ambiance.sound_play(thesound, pp, 1.0, 50)
- ambiance.sound_play(thesound, tp, 1.0, 50)
- if pfx then
- preload_tp.spawn_particles(pp)
- preload_tp.spawn_particles(tp)
- preload_tp.spawn_spinup_particles(vector_round(tp), 3)
- end
- end
- function preload_tp.wait_for_timeout(parameters)
- local start_time = parameters.start_time
- local total_time = parameters.total_time
- local end_time = os.time()
- if (end_time - start_time) < total_time then
- minetest.after(1, function()
- preload_tp.wait_for_timeout(parameters)
- end)
- return
- end
- preload_tp.finalize(parameters)
- end
- function preload_tp.spawn_spinup_particles(pos, time)
- local xd = 1
- local zd = 1
- minetest.add_particlespawner({
- amount = 160,
- time = time,
- minpos = {x=pos.x-xd, y=pos.y-0, z=pos.z-zd},
- maxpos = {x=pos.x+xd, y=pos.y+2, z=pos.z+zd},
- minvel = {x=0, y=-1, z=0},
- maxvel = {x=0, y=1, z=0},
- minacc = {x=0, y=-1, z=0},
- maxacc = {x=0, y=1, z=0},
- minexptime = 0.5,
- maxexptime = 1.5,
- minsize = 0.5,
- maxsize = 2,
- collisiondetection = false,
- vertical = true,
- texture = "default_coal_lump.png",
- glow = 14,
- })
- minetest.add_particlespawner({
- amount = 160,
- time = time,
- minpos = {x=pos.x-xd, y=pos.y-0, z=pos.z-zd},
- maxpos = {x=pos.x+xd, y=pos.y+2, z=pos.z+zd},
- minvel = {x=-1, y=-1, z=-1},
- maxvel = {x=1, y=1, z=1},
- minacc = {x=-1, y=-1, z=-1},
- maxacc = {x=1, y=1, z=1},
- minexptime = 0.5,
- maxexptime = 1.5,
- minsize = 0.5,
- maxsize = 2,
- collisiondetection = false,
- texture = "default_mese_crystal.png",
- glow = 14,
- })
- end
- -- API function. Preload the area, then teleport the player there
- -- only if they have not moved during the preload. After a successful
- -- teleport, execute the callback function if it's not nil.
- function preload_tp.execute(parameters)
- -- Copy table so we don't end up modifying the original.
- parameters = table.copy(parameters)
- -- Set default parameters.
- parameters.player_name = parameters.player_name or ""
- parameters.target_position = parameters.target_position or {x=0, y=0, z=0}
- parameters.emerge_radius = parameters.emerge_radius or 16
- parameters.pre_teleport_callback = parameters.pre_teleport_callback or nil
- parameters.post_teleport_callback = parameters.post_teleport_callback or nil
- parameters.callback_param = parameters.callback_param or nil
- parameters.force_teleport = parameters.force_teleport or false
- parameters.teleport_sound = parameters.teleport_sound or nil
- parameters.send_blocks = parameters.send_blocks or false
- parameters.particle_effects = parameters.particle_effects or false
- local pname = parameters.player_name
- local tpos = parameters.target_position
- local radius = parameters.emerge_radius
- local pre_cb = parameters.pre_teleport_callback
- local post_cb = parameters.post_teleport_callback
- local cb_param = parameters.callback_param
- local force = parameters.force_teleport
- local tpsound = parameters.teleport_sound
- local sendblocks = parameters.send_blocks
- local pfx = parameters.particle_effects
- local player = minetest.get_player_by_name(pname)
- if not player or not player:is_player() then
- return
- end
- local tp = table.copy(tpos)
- local pp = player:get_pos()
- local start_time = os.time()
- -- Time to teleport depends on distance.
- local total_time = math_floor(vector_distance(pp, tp) / 1000)
- if total_time < 2 then
- total_time = 2
- end
- parameters.start_position = pp
- parameters.start_time = start_time
- parameters.total_time = total_time
- minetest.log("action", pname .. " initiates teleport to " .. minetest.pos_to_string(tp))
- if pfx then
- preload_tp.spawn_spinup_particles(vector_round(pp), total_time + 2)
- preload_tp.spawn_spinup_particles(vector_round(tp), total_time + 1)
- end
- -- Build callback function. When the map is loaded, we can teleport the player.
- local cb = function(blockpos, action, calls_remaining, parameters)
- -- Check if there was an error.
- -- This avoids false error reports if the area to be generated exceeds the max map edge.
- -- Update: actually it doesn't?
- if action == core.EMERGE_CANCELLED or action == core.EMERGE_ERRORED then
- minetest.chat_send_player(pname, "# Server: Internal error, try again or report.")
- return
- end
- -- Send blocks to client as soon as they're available; looks better that way.
- if sendblocks then
- if blockpos then
- local pref = minetest.get_player_by_name(pname)
- if pref then
- pref:send_mapblock(blockpos)
- end
- end
- end
- -- We don't do anything until the last callback.
- if calls_remaining ~= 0 then
- return
- end
- if not force then
- preload_tp.wait_for_timeout(parameters)
- return
- end
- -- Forced teleport always teleports as soon as possible!
- preload_tp.finalize(parameters)
- end
- local minp = vector.add(tp, vector.new(-radius, -radius, -radius))
- local maxp = vector.add(tp, vector.new(radius, radius, radius))
- -- Emerge the target area. Once emergence is complete player can be teleported.
- minetest.chat_send_player(pname, "# Server: Spatially translating! Stand by.")
- minetest.emerge_area(minp, maxp, cb, parameters)
- end
- local particles = {
- amount = 20,
- time = 1,
- -- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
- minpos = {x=0, y=0, z=0},
- maxpos = {x=0, y=0, z=0},
- minvel = {x=0, y=6, z=0},
- maxvel = {x=0, y=9, z=0},
- minacc = {x=0, y=2, z=0},
- maxacc = {x=0, y=4, z=0},
- minexptime = 0.5,
- maxexptime = 1,
- minsize = 0.2,
- maxsize = 0.8,
- -- ^ The particle's properties are random values in between the bounds:
- -- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
- -- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
- collisiondetection = false,
- -- ^ collisiondetection: if true uses collision detection
- collision_removal = false,
- -- ^ collision_removal: if true then particle is removed when it collides,
- -- ^ requires collisiondetection = true to have any effect
- --attached = ObjectRef,
- -- ^ attached: if defined, particle positions, velocities and accelerations
- -- ^ are relative to this object's position and yaw.
- vertical = false,
- -- ^ vertical: if true faces player using y axis only
- texture = "teleports_teleport_top.png",
- -- ^ Uses texture (string)
- --playername = "singleplayer"
- -- ^ Playername is optional, if specified spawns particle only on the player's client
- }
- function preload_tp.spawn_particles(pos)
- particles.minpos = vector.add(pos, {x=-1.0, y=-1.0, z=-1.0})
- particles.maxpos = vector.add(pos, {x=1.0, y=-1.0, z=1.0})
- minetest.add_particlespawner(particles)
- end
- if not preload_tp.run_once then
- local c = "preload_tp:core"
- local f = preload_tp.modpath .. "/init.lua"
- reload.register_file(c, f, false)
- preload_tp.run_once = true
- end
|