Easy to use travel teleporter stones for world travel.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
7.9KB

  1. --[[
  2. Copyright (C) 2015 - Auke Kok <sofar@foo-projects.org>
  3. "warps" is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as
  5. published by the Free Software Foundation; either version 2.1
  6. of the license, or (at your option) any later version.
  7. --]]
  8. warps = {}
  9. warps_queue = {}
  10. queue_state = 0
  11. local warps_freeze = 5
  12. -- t = time in usec
  13. -- p = player obj
  14. -- w = warp name
  15. local S = minetest.get_mod_storage()
  16. assert(S, "mod_storage is required")
  17. -- import warps or load
  18. local store = S:get("warps")
  19. local worldpath = minetest.get_worldpath()
  20. if store then
  21. warps = minetest.deserialize(store)
  22. else
  23. local fh,err = io.open(worldpath .. "/warps.txt", "r")
  24. if err then
  25. minetest.log("action", "[warps] loaded ")
  26. return
  27. end
  28. while true do
  29. local line = fh:read()
  30. if line == nil then
  31. break
  32. end
  33. local paramlist = string.split(line, " ")
  34. local w = {
  35. name = paramlist[1],
  36. x = tonumber(paramlist[2]),
  37. y = tonumber(paramlist[3]),
  38. z = tonumber(paramlist[4]),
  39. yaw = tonumber(paramlist[5]),
  40. pitch = tonumber(paramlist[6])
  41. }
  42. table.insert(warps, w)
  43. end
  44. fh:close()
  45. minetest.log("action", "[warps] converted warps to mod_storage. Please delete 'warps.txt'.")
  46. S:set_string("warps", minetest.serialize(warps))
  47. end
  48. local function lookup_warp(name)
  49. for i = 1,table.getn(warps) do
  50. if warps[i].name == name then
  51. return warps[i]
  52. end
  53. end
  54. end
  55. local function round_digits(n, digits)
  56. digits = digits or 0
  57. local multi = math.pow(10, digits)
  58. n = n * multi
  59. if n > 0 then
  60. return math.floor(n + 0.5) / multi
  61. else
  62. return math.ceil(n - 0.5) / multi
  63. end
  64. end
  65. local warp = function(player, dest)
  66. local warp = lookup_warp(dest)
  67. if not warp then
  68. minetest.chat_send_player(player:get_player_name(), "Unknown warp \"" .. dest .. "\"")
  69. return
  70. end
  71. local pos = vector.new(warp)
  72. pos.y = pos.y + 0.5
  73. player:setpos(pos)
  74. player:set_look_horizontal(warp.yaw)
  75. player:set_look_vertical(warp.pitch)
  76. minetest.chat_send_player(player:get_player_name(), "Warped to \"" .. dest .. "\"")
  77. minetest.log("action", player:get_player_name() .. " warped to \"" .. dest .. "\"")
  78. minetest.sound_play("warps_plop", {pos = pos})
  79. end
  80. do_warp_queue = function()
  81. if table.getn(warps_queue) == 0 then
  82. queue_state = 0
  83. return
  84. end
  85. local t = minetest.get_us_time()
  86. for i = table.getn(warps_queue),1,-1 do
  87. local e = warps_queue[i]
  88. if e.p:getpos() then
  89. if vector.equals(e.p:getpos(), e.pos) then
  90. if t > e.t then
  91. warp(e.p, e.w)
  92. table.remove(warps_queue, i)
  93. end
  94. else
  95. minetest.sound_stop(e.sh)
  96. minetest.chat_send_player(e.p:get_player_name(),
  97. "You have to stand still for " .. warps_freeze .. " seconds!")
  98. table.remove(warps_queue, i)
  99. end
  100. end
  101. end
  102. if table.getn(warps_queue) == 0 then
  103. queue_state = 0
  104. return
  105. end
  106. minetest.after(1, do_warp_queue)
  107. end
  108. local warp_queue_add = function(player, dest)
  109. table.insert(warps_queue, {
  110. t = minetest.get_us_time() + (warps_freeze * 1000000),
  111. pos = player:getpos(),
  112. p = player,
  113. w = dest,
  114. sh = minetest.sound_play("warps_woosh", { pos = player:getpos() })
  115. })
  116. minetest.chat_send_player(player:get_player_name(), "Don't move for " .. warps_freeze .. " seconds!")
  117. if queue_state == 0 then
  118. queue_state = 1
  119. minetest.after(1, do_warp_queue)
  120. end
  121. -- attempt to emerge the target area before the player gets there
  122. local pos = vector.new(lookup_warp(dest))
  123. minetest.get_voxel_manip():read_from_map(pos, pos)
  124. if not minetest.get_node_or_nil(pos) then
  125. minetest.emerge_area(vector.subtract(pos, 80), vector.add(pos, 80))
  126. end
  127. -- force mapblock send to player, if supported
  128. if player.send_mapblock then
  129. player:send_mapblock(vector.divide(pos, 16))
  130. end
  131. end
  132. minetest.register_privilege("warp_admin", {
  133. description = "Allows modification of warp points",
  134. give_to_singleplayer = true,
  135. default = false
  136. })
  137. minetest.register_privilege("warp_user", {
  138. description = "Allows use of warp points",
  139. give_to_singleplayer = true,
  140. default = true
  141. })
  142. minetest.register_chatcommand("setwarp", {
  143. params = "name",
  144. description = "Set a warp location to the players location",
  145. privs = { warp_admin = true },
  146. func = function(name, param)
  147. param = param:gsub("%W", "")
  148. if param == "" then
  149. return false, "Cannot set warp: Name missing."
  150. end
  151. local h = "Created"
  152. for i = 1,table.getn(warps) do
  153. if warps[i].name == param then
  154. table.remove(warps, i)
  155. h = "Changed"
  156. break
  157. end
  158. end
  159. local player = minetest.get_player_by_name(name)
  160. local pos = vector.round(player:getpos())
  161. table.insert(warps, {
  162. name = param,
  163. x = pos.x,
  164. y = pos.y,
  165. z = pos.z,
  166. yaw = round_digits(player:get_look_horizontal(), 3),
  167. pitch = round_digits(player:get_look_vertical(), 3)
  168. })
  169. S:set_string("warps", minetest.serialize(warps))
  170. minetest.log("action", name .. " " .. h .. " warp \"" .. param .. "\": " ..
  171. pos.x .. ", " .. pos.y .. ", " .. pos.z)
  172. return true, h .. " warp \"" .. param .. "\""
  173. end,
  174. })
  175. minetest.register_chatcommand("delwarp", {
  176. params = "name",
  177. description = "Set a warp location to the players location",
  178. privs = { warp_admin = true },
  179. func = function(name, param)
  180. for i = 1,table.getn(warps) do
  181. if warps[i].name == param then
  182. table.remove(warps, i)
  183. minetest.log("action", name .. " removed warp \"" .. param .. "\"")
  184. return true, "Removed warp \"" .. param .. "\""
  185. end
  186. end
  187. return false, "Unknown warp location \"" .. param .. "\""
  188. end,
  189. })
  190. minetest.register_chatcommand("listwarps", {
  191. params = "name",
  192. description = "List known warp locations",
  193. privs = { warp_user = true },
  194. func = function(name, param)
  195. local s = "List of known warp locations:\n"
  196. for i = 1,table.getn(warps) do
  197. s = s .. "- " .. warps[i].name .. "\n"
  198. end
  199. return true, s
  200. end
  201. })
  202. minetest.register_chatcommand("warp", {
  203. params = "name",
  204. description = "Warp to a warp location",
  205. privs = { warp_user = true },
  206. func = function(name, param)
  207. local player = minetest.get_player_by_name(name)
  208. if not minetest.check_player_privs(player, {warp_admin = true}) then
  209. warp_queue_add(player, param)
  210. else
  211. warp(player, param)
  212. end
  213. end
  214. })
  215. minetest.register_node("warps:warpstone", {
  216. visual = "mesh",
  217. mesh = "warps_warpstone.obj",
  218. description = "A Warp Stone",
  219. tiles = { "warps_warpstone.png" },
  220. drawtype = "mesh",
  221. sunlight_propagates = true,
  222. walkable = false,
  223. paramtype = "light",
  224. groups = { choppy=3 },
  225. light_source = 8,
  226. diggable = false,
  227. selection_box = {
  228. type = "fixed",
  229. fixed = {-0.25, -0.5, -0.25, 0.25, 0.5, 0.25}
  230. },
  231. on_construct = function(pos)
  232. local meta = minetest.get_meta(pos)
  233. meta:set_string("formspec",
  234. "field[destination;Warp Destination;]")
  235. meta:set_string("infotext", "Uninitialized Warp Stone")
  236. end,
  237. on_receive_fields = function(pos, formname, fields, sender)
  238. if not minetest.check_player_privs(sender:get_player_name(), {warp_admin = true}) then
  239. minetest.chat_send_player(sender:get_player_name(), "You do not have permission to modify warp stones")
  240. return false
  241. end
  242. if not fields.destination then
  243. return
  244. end
  245. local meta = minetest.get_meta(pos)
  246. meta:set_string("formspec",
  247. "field[destination;Warp Destination;" .. fields.destination .. "]")
  248. meta:set_string("infotext", "Warp stone to " .. fields.destination)
  249. meta:set_string("warps_destination", fields.destination)
  250. minetest.log("action", sender:get_player_name() .. " changed warp stone to \"" .. fields.destination .. "\"")
  251. end,
  252. on_punch = function(pos, node, puncher, pointed_thingo)
  253. if puncher:get_player_control().sneak and
  254. minetest.check_player_privs(puncher:get_player_name(), {warp_admin = true}) then
  255. minetest.remove_node(pos)
  256. minetest.chat_send_player(puncher:get_player_name(), "Warp stone removed!")
  257. return
  258. end
  259. local meta = minetest.get_meta(pos)
  260. local destination = meta:get_string("warps_destination")
  261. if destination == "" then
  262. minetest.chat_send_player(puncher:get_player_name(),
  263. "Unknown warp location for this warp stone, cannot warp!")
  264. return false
  265. end
  266. warp_queue_add(puncher, destination)
  267. end,
  268. })