init.lua 7.6 KB

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