init.lua 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. preload_tp = preload_tp or {}
  2. preload_tp.modpath = minetest.get_modpath("preload_tp")
  3. -- Localize for performance.
  4. local vector_distance = vector.distance
  5. local vector_round = vector.round
  6. local math_floor = math.floor
  7. function preload_tp.finalize(parameters)
  8. local pname = parameters.player_name
  9. local force = parameters.force_teleport
  10. local pp = parameters.start_position
  11. local tp = parameters.target_position
  12. local pre_cb = parameters.pre_teleport_callback
  13. local post_cb = parameters.post_teleport_callback
  14. local cb_param = parameters.callback_param
  15. local tpsound = parameters.teleport_sound
  16. local pfx = parameters.particle_effects
  17. -- Find the player.
  18. local player = minetest.get_player_by_name(pname)
  19. if not player or not player:is_player() then
  20. -- The player left, or something. Do not teleport them.
  21. minetest.log("action", pname .. " left the game while a teleport callback was in progress")
  22. return
  23. end
  24. -- The player may optionally be force-teleported.
  25. if not force then
  26. -- Did the player move?
  27. if vector_distance(pp, player:get_pos()) > 1.5 then
  28. minetest.chat_send_player(pname, "# Server: Transport error. You cannot move while a transport is in progress.")
  29. return
  30. end
  31. -- If the player killed themselves, do not teleport them.
  32. if player:get_hp() == 0 then
  33. minetest.chat_send_player(pname, "# Server: Transport error. You are dead.")
  34. return
  35. end
  36. end
  37. -- But we must never teleport a player who is attached.
  38. if default.player_attached[pname] then
  39. minetest.chat_send_player(pname, "# Server: Transport error. Player attached!")
  40. return
  41. end
  42. -- Execute the callback function, if everything else succeeded.
  43. if pre_cb then
  44. -- If the pre-teleport callback returns 'success' then that
  45. -- signals that the teleport must be aborted for some reason.
  46. if pre_cb(cb_param) then
  47. minetest.chat_send_player(pname, "# Server: Transport canceled.")
  48. return
  49. end
  50. end
  51. minetest.log("action", "executing teleport callback for " .. pname .. "!")
  52. -- Teleport player only if they didn't move (or teleporting is forced).
  53. wield3d.on_teleport()
  54. rc.notify_realm_update(player, tp)
  55. player:set_pos(tp)
  56. minetest.log("action", pname .. " actually teleports to " .. minetest.pos_to_string(tp))
  57. -- Execute the callback function, if everything else succeeded.
  58. if post_cb then
  59. post_cb(cb_param)
  60. end
  61. local thesound = "teleport"
  62. if type(tpsound) == "string" then
  63. thesound = tpsound
  64. end
  65. -- The teleport sound, played @ old & new locations.
  66. ambiance.sound_play(thesound, pp, 1.0, 50)
  67. ambiance.sound_play(thesound, tp, 1.0, 50)
  68. if pfx then
  69. preload_tp.spawn_particles(pp)
  70. preload_tp.spawn_particles(tp)
  71. preload_tp.spawn_spinup_particles(vector_round(tp), 3)
  72. end
  73. end
  74. function preload_tp.wait_for_timeout(parameters)
  75. local start_time = parameters.start_time
  76. local total_time = parameters.total_time
  77. local end_time = os.time()
  78. if (end_time - start_time) < total_time then
  79. minetest.after(1, function()
  80. preload_tp.wait_for_timeout(parameters)
  81. end)
  82. return
  83. end
  84. preload_tp.finalize(parameters)
  85. end
  86. function preload_tp.spawn_spinup_particles(pos, time)
  87. local xd = 1
  88. local zd = 1
  89. minetest.add_particlespawner({
  90. amount = 160,
  91. time = time,
  92. minpos = {x=pos.x-xd, y=pos.y-0, z=pos.z-zd},
  93. maxpos = {x=pos.x+xd, y=pos.y+2, z=pos.z+zd},
  94. minvel = {x=0, y=-1, z=0},
  95. maxvel = {x=0, y=1, z=0},
  96. minacc = {x=0, y=-1, z=0},
  97. maxacc = {x=0, y=1, z=0},
  98. minexptime = 0.5,
  99. maxexptime = 1.5,
  100. minsize = 0.5,
  101. maxsize = 2,
  102. collisiondetection = false,
  103. vertical = true,
  104. texture = "default_coal_lump.png",
  105. glow = 14,
  106. })
  107. minetest.add_particlespawner({
  108. amount = 160,
  109. time = time,
  110. minpos = {x=pos.x-xd, y=pos.y-0, z=pos.z-zd},
  111. maxpos = {x=pos.x+xd, y=pos.y+2, z=pos.z+zd},
  112. minvel = {x=-1, y=-1, z=-1},
  113. maxvel = {x=1, y=1, z=1},
  114. minacc = {x=-1, y=-1, z=-1},
  115. maxacc = {x=1, y=1, z=1},
  116. minexptime = 0.5,
  117. maxexptime = 1.5,
  118. minsize = 0.5,
  119. maxsize = 2,
  120. collisiondetection = false,
  121. texture = "default_mese_crystal.png",
  122. glow = 14,
  123. })
  124. end
  125. -- API function. Preload the area, then teleport the player there
  126. -- only if they have not moved during the preload. After a successful
  127. -- teleport, execute the callback function if it's not nil.
  128. function preload_tp.execute(parameters)
  129. -- Copy table so we don't end up modifying the original.
  130. parameters = table.copy(parameters)
  131. -- Set default parameters.
  132. parameters.player_name = parameters.player_name or ""
  133. parameters.target_position = parameters.target_position or {x=0, y=0, z=0}
  134. parameters.emerge_radius = parameters.emerge_radius or 16
  135. parameters.pre_teleport_callback = parameters.pre_teleport_callback or nil
  136. parameters.post_teleport_callback = parameters.post_teleport_callback or nil
  137. parameters.callback_param = parameters.callback_param or nil
  138. parameters.force_teleport = parameters.force_teleport or false
  139. parameters.teleport_sound = parameters.teleport_sound or nil
  140. parameters.send_blocks = parameters.send_blocks or false
  141. parameters.particle_effects = parameters.particle_effects or false
  142. local pname = parameters.player_name
  143. local tpos = parameters.target_position
  144. local radius = parameters.emerge_radius
  145. local pre_cb = parameters.pre_teleport_callback
  146. local post_cb = parameters.post_teleport_callback
  147. local cb_param = parameters.callback_param
  148. local force = parameters.force_teleport
  149. local tpsound = parameters.teleport_sound
  150. local sendblocks = parameters.send_blocks
  151. local pfx = parameters.particle_effects
  152. local player = minetest.get_player_by_name(pname)
  153. if not player or not player:is_player() then
  154. return
  155. end
  156. local tp = table.copy(tpos)
  157. local pp = player:get_pos()
  158. local start_time = os.time()
  159. -- Time to teleport depends on distance.
  160. local total_time = math_floor(vector_distance(pp, tp) / 1000)
  161. if total_time < 2 then
  162. total_time = 2
  163. end
  164. parameters.start_position = pp
  165. parameters.start_time = start_time
  166. parameters.total_time = total_time
  167. minetest.log("action", pname .. " initiates teleport to " .. minetest.pos_to_string(tp))
  168. if pfx then
  169. preload_tp.spawn_spinup_particles(vector_round(pp), total_time + 2)
  170. preload_tp.spawn_spinup_particles(vector_round(tp), total_time + 1)
  171. end
  172. -- Build callback function. When the map is loaded, we can teleport the player.
  173. local cb = function(blockpos, action, calls_remaining, parameters)
  174. -- Check if there was an error.
  175. -- This avoids false error reports if the area to be generated exceeds the max map edge.
  176. -- Update: actually it doesn't?
  177. if action == core.EMERGE_CANCELLED or action == core.EMERGE_ERRORED then
  178. minetest.chat_send_player(pname, "# Server: Internal error, try again or report.")
  179. return
  180. end
  181. -- Send blocks to client as soon as they're available; looks better that way.
  182. if sendblocks then
  183. if blockpos then
  184. local pref = minetest.get_player_by_name(pname)
  185. if pref then
  186. pref:send_mapblock(blockpos)
  187. end
  188. end
  189. end
  190. -- We don't do anything until the last callback.
  191. if calls_remaining ~= 0 then
  192. return
  193. end
  194. if not force then
  195. preload_tp.wait_for_timeout(parameters)
  196. return
  197. end
  198. -- Forced teleport always teleports as soon as possible!
  199. preload_tp.finalize(parameters)
  200. end
  201. local minp = vector.add(tp, vector.new(-radius, -radius, -radius))
  202. local maxp = vector.add(tp, vector.new(radius, radius, radius))
  203. -- Emerge the target area. Once emergence is complete player can be teleported.
  204. minetest.chat_send_player(pname, "# Server: Spatially translating! Stand by.")
  205. minetest.emerge_area(minp, maxp, cb, parameters)
  206. end
  207. local particles = {
  208. amount = 20,
  209. time = 1,
  210. -- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
  211. minpos = {x=0, y=0, z=0},
  212. maxpos = {x=0, y=0, z=0},
  213. minvel = {x=0, y=6, z=0},
  214. maxvel = {x=0, y=9, z=0},
  215. minacc = {x=0, y=2, z=0},
  216. maxacc = {x=0, y=4, z=0},
  217. minexptime = 0.5,
  218. maxexptime = 1,
  219. minsize = 0.2,
  220. maxsize = 0.8,
  221. -- ^ The particle's properties are random values in between the bounds:
  222. -- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
  223. -- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
  224. collisiondetection = false,
  225. -- ^ collisiondetection: if true uses collision detection
  226. collision_removal = false,
  227. -- ^ collision_removal: if true then particle is removed when it collides,
  228. -- ^ requires collisiondetection = true to have any effect
  229. --attached = ObjectRef,
  230. -- ^ attached: if defined, particle positions, velocities and accelerations
  231. -- ^ are relative to this object's position and yaw.
  232. vertical = false,
  233. -- ^ vertical: if true faces player using y axis only
  234. texture = "teleports_teleport_top.png",
  235. -- ^ Uses texture (string)
  236. --playername = "singleplayer"
  237. -- ^ Playername is optional, if specified spawns particle only on the player's client
  238. }
  239. function preload_tp.spawn_particles(pos)
  240. particles.minpos = vector.add(pos, {x=-1.0, y=-1.0, z=-1.0})
  241. particles.maxpos = vector.add(pos, {x=1.0, y=-1.0, z=1.0})
  242. minetest.add_particlespawner(particles)
  243. end
  244. if not preload_tp.run_once then
  245. local c = "preload_tp:core"
  246. local f = preload_tp.modpath .. "/init.lua"
  247. reload.register_file(c, f, false)
  248. preload_tp.run_once = true
  249. end