1 local TELEPORT_MAX_DIST
= 2606 -- maximum teleport distance in all directions
2 local TELEPORT_SHOW_MAX
= 6 -- maximum teleports shown in formspec
3 local TELEPORT_FUEL
= "default:mossycobble" -- fuel item (note: if you change this,
4 -- you also need to change formspec text)
5 local TELEPORT_INITFUEL
= 30 -- initial fuel items for new teleports
8 if minetest
.get_translator
then
9 S
= minetest
.get_translator("teleports")
11 -- Compability translator code to support MT 0.4, which doesn't support
12 -- translations for mods.
14 local function translate(textdomain
, str
, ...)
15 local arg
= {n
=select('#', ...), ...}
16 return str
:gsub("@(.)", function(matched
)
17 local c
= string.byte(matched
)
18 if string.byte("1") <= c
and c
<= string.byte("9") then
19 return arg
[c
- string.byte("0")]
26 S
= function(textdomain
)
27 return function(str
, ...)
28 return translate(textdomain
or "", str
, ...)
34 local F
= minetest
.formspec_escape
37 teleports
.teleports
= {}
38 teleports
.lastplayername
=""
39 teleports
.filename
= minetest
.get_worldpath() .. "/teleports.txt"
41 function teleports
:save()
42 local datastring
= minetest
.serialize(self
.teleports
)
43 if not datastring
then
46 local file
, err
= io
.open(self
.filename
, "w")
50 file
:write(datastring
)
53 function teleports
:load()
54 local file
, err
= io
.open(self
.filename
, "r")
59 self
.teleports
= minetest
.deserialize(file
:read("*all"))
60 if type(self
.teleports
) ~= "table" then
65 function teleports
:find_nearby(pos
, count
)
67 for i
= #teleports
.teleports
, 1, -1 do
68 local EachTeleport
= teleports
.teleports
[i
]
69 if not vector
.equals(EachTeleport
.pos
, pos
) and vector
.distance(EachTeleport
.pos
, pos
) < TELEPORT_MAX_DIST
then
70 table.insert(nearby
, EachTeleport
)
71 if #nearby
>=count
then
78 function teleports
.animate(pos
, playername
)
79 minetest
.add_particlespawner({
82 minpos
= {x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
-1},
83 maxpos
= {x
=pos
.x
+1, y
=pos
.y
+3, z
=pos
.z
+1},
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},
92 collisiondetection
= false,
94 texture
= "default_diamond.png",
95 playername
= playername
,
97 minetest
.add_particlespawner({
100 minpos
= {x
=pos
.x
-1, y
=pos
.y
, z
=pos
.z
-1},
101 maxpos
= {x
=pos
.x
+1, y
=pos
.y
+3, z
=pos
.z
+1},
102 minvel
= {x
=0, y
=-1, z
=0},
103 maxvel
= {x
=0, y
=1, z
=0},
104 minacc
= {x
=0, y
=-1, z
=0},
105 maxacc
= {x
=0, y
=1, z
=0},
110 collisiondetection
= false,
112 texture
= "default_diamond.png",
115 function teleports
.teleportate(parameters
)
116 local pos1
,pos2
,playername
= parameters
[1],parameters
[2],parameters
[3]
118 local player
= minetest
.get_player_by_name(playername
)
119 if player
and player
:is_player() and playername
~=teleports
.lastplayername
then
120 local pos
= player
:getpos()
121 if vector
.distance(pos
, {x
=pos1
.x
,y
=pos1
.y
+0.5,z
=pos1
.z
}) < 0.52 then
122 if math
.random(1, 100) > 5 then
123 teleports
.lastplayername
= playername
124 player
:setpos({x
=pos2
.x
,y
=pos2
.y
+0.5,z
=pos2
.z
})
126 player
:setpos({x
=pos2
.x
-5+math
.random(1, 10),y
=pos2
.y
+3,z
=pos2
.z
-5+math
.random(1, 10)})
131 function teleports
.do_teleporting(pos1
, pos2
, playername
, delay
)
133 teleports
.teleportate(pos1
, pos2
, playername
)
135 teleports
.animate(pos1
, playername
)
136 minetest
.after(delay
, teleports
.teleportate
, {pos1
, pos2
, playername
})
139 teleports
.set_formspec
= function(pos
, shown_teleports
)
140 local meta
= minetest
.get_meta(pos
)
141 local node
= minetest
.get_node(pos
)
146 for i
, EachTeleport
in ipairs( shown_teleports
) do
151 if EachTeleport
["name"] then
152 buttons
= buttons
.."button_exit["..x
..","..y
..";3.5,0.5;tp"..i
..";"..F(S("GO>@1",EachTeleport
.name
)).."]";
154 buttons
= buttons
.."button_exit["..x
..","..y
..";3.5,0.5;tp"..i
..";"..F(S("GO>@1,@2,@3",EachTeleport
.pos
.x
,EachTeleport
.pos
.y
,EachTeleport
.pos
.z
)).."]";
160 if buttons
== "" then
161 label
= S("No teleport in range!")
163 label
= S("Go to available teleports! Use mossy cobblestone as fuel!")
166 meta
:set_string("formspec", "size[8,9;]"
167 .."label[0,0;"..F(label
).."]"
168 .."list[current_name;price;0,1;1,1;]"
170 .."button_exit[1,4;2,0.5;cancel;"..F(S("Cancel")).."]"
171 .."list[current_player;main;0,5;8,4;]")
173 teleports
.on_receive_fields
= function(pos
, formname
, fields
, player
)
174 local meta
= minetest
.env
:get_meta(pos
);
175 local inv
= meta
:get_inventory();
176 local price
= {name
=TELEPORT_FUEL
, count
=1, wear
=0, metadata
=""}
177 if fields
.tp1
or fields
.tp2
or fields
.tp3
or fields
.tp4
or fields
.tp5
or fields
.tp6
then
178 if inv
:contains_item("price", price
) then
179 inv
:remove_item("price", price
);
180 teleports
.lastplayername
= ""
181 local available
= teleports
:find_nearby(pos
, TELEPORT_SHOW_MAX
)
182 if player
~= nil and player
:is_player() then
183 local playerpos
= player
:getpos()
184 for i
=1, TELEPORT_SHOW_MAX
do
185 if fields
["tp"..i
] and #available
>i
-1 then
186 teleports
.do_teleporting(playerpos
, available
[i
].pos
, player
:get_player_name())
191 teleports
.set_formspec(pos
, available
)
195 teleports
.allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
196 if listname
=="price" and stack
:get_name()==TELEPORT_FUEL
then
202 teleports
.allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
209 minetest
.register_node("teleports:teleport", {
210 description
= S("Teleport"),
211 drawtype
= "glasslike",
212 tiles
= {"teleports_teleport_top.png"},
213 is_ground_content
= false,
214 light_source
= minetest
.LIGHT_MAX
,
215 groups
= {cracky
=1, level
=3},
216 drop
= 'default:diamond',
217 sounds
= default
.node_sound_stone_defaults(),
218 after_place_node
= function(pos
, placer
)
219 if placer
and placer
:is_player() then
220 local meta
= minetest
.env
:get_meta(pos
)
221 local inv
= meta
:get_inventory()
222 inv
:set_size("price", 1)
223 if TELEPORT_INITFUEL
> 0 then
224 local initialcharge
= {name
=TELEPORT_FUEL
, count
=TELEPORT_INITFUEL
, wear
=0, metadata
=""}
225 inv
:add_item("price", initialcharge
)
228 -- Add formspec to self
229 local near_teleports
= teleports
:find_nearby(pos
, TELEPORT_SHOW_MAX
)
230 teleports
.set_formspec(pos
, near_teleports
)
232 local sign_pos
= minetest
.find_node_near(pos
, 1, {"default:sign_wall_wood", "default:sign_wall_steel"})
234 local sign_meta
= minetest
.env
:get_meta(sign_pos
)
235 local sign_text
= sign_meta
:get_string("text")
236 local secret_name
= sign_text
:sub(0, 16)
237 table.insert(teleports
.teleports
, {pos
=vector
.round(pos
), name
=secret_name
})
239 table.insert(teleports
.teleports
, {pos
=vector
.round(pos
)})
242 -- Update formspec of target teleports
243 for n
=1, #near_teleports
do
244 local pos2
= near_teleports
[n
].pos
245 local near_teleports2
= teleports
:find_nearby(pos2
, TELEPORT_SHOW_MAX
)
246 teleports
.set_formspec(pos2
, near_teleports2
)
252 on_destruct
= function(pos
)
253 -- Update list of teleports on destruction
254 local near_teleports
= teleports
:find_nearby(pos
, TELEPORT_SHOW_MAX
)
256 for i
, EachTeleport
in ipairs(teleports
.teleports
) do
257 if vector
.equals(EachTeleport
.pos
, pos
) then
258 table.remove(teleports
.teleports
, i
)
263 -- Update formspecs of affected teleports
264 for n
=1, #near_teleports
do
265 local pos2
= near_teleports
[n
].pos
266 local near_teleports2
= teleports
:find_nearby(pos2
, TELEPORT_SHOW_MAX
)
267 teleports
.set_formspec(pos2
, near_teleports2
)
270 on_receive_fields
= teleports
.on_receive_fields
,
271 allow_metadata_inventory_put
= teleports
.allow_metadata_inventory_put
,
272 allow_metadata_inventory_take
= teleports
.allow_metadata_inventory_take
,
276 minetest
.override_item("default:diamondblock", {
277 on_place
= function(itemstack
, placer
, pointed_thing
)
278 local stack
= ItemStack("default:diamondblock")
279 local pos
= pointed_thing
.above
281 minetest
.get_node({x
=pos
.x
+1,y
=pos
.y
,z
=pos
.z
}).name
=="default:diamondblock" and
282 minetest
.get_node({x
=pos
.x
+1,y
=pos
.y
,z
=pos
.z
+1}).name
=="default:diamondblock" and
283 minetest
.get_node({x
=pos
.x
+1,y
=pos
.y
,z
=pos
.z
-1}).name
=="default:diamondblock" and
284 minetest
.get_node({x
=pos
.x
-1,y
=pos
.y
,z
=pos
.z
}).name
=="default:diamondblock" and
285 minetest
.get_node({x
=pos
.x
-1,y
=pos
.y
,z
=pos
.z
+1}).name
=="default:diamondblock" and
286 minetest
.get_node({x
=pos
.x
-1,y
=pos
.y
,z
=pos
.z
-1}).name
=="default:diamondblock" and
287 minetest
.get_node({x
=pos
.x
,y
=pos
.y
,z
=pos
.z
+1}).name
=="default:diamondblock" and
288 minetest
.get_node({x
=pos
.x
,y
=pos
.y
,z
=pos
.z
-1}).name
=="default:diamondblock"
290 stack
= ItemStack("teleports:teleport")
292 local ret
= minetest
.item_place(stack
, placer
, pointed_thing
)
296 return ItemStack("default:diamondblock "..itemstack
:get_count()-(1-ret
:get_count()))
301 minetest
.register_abm({
302 nodenames
= {"teleports:teleport"},
305 action
= function(pos
)
306 local objectsnear
=minetest
.get_objects_inside_radius({x
=pos
.x
,y
=pos
.y
+0.5,z
=pos
.z
}, 0.52);
307 if #objectsnear
>0 then
308 local player
= objectsnear
[1];
309 -- check only first two objekts then give up
310 if #objectsnear
>1 and not player
:is_player() then
311 player
= objectsnear
[2];
313 if player
:is_player() and player
:get_player_name()~=teleports
.lastplayername
then
314 local positions
= teleports
:find_nearby(pos
, 11)
316 local key
= math
.random(1, #positions
)
318 local view
= player
:get_look_dir();
319 local dist
, distmin
; distmin
= 99;
320 for i
=1,#positions
do -- find teleport closest to where player is looking
321 dir
= {x
=positions
[i
].pos
.x
-pos
.x
,y
=positions
[i
].pos
.y
-pos
.y
,z
=positions
[i
].pos
.z
-pos
.z
};
322 dirmag
= math
.sqrt(dir
.x
*dir
.x
+dir
.y
*dir
.y
+dir
.z
*dir
.z
); if dirmag
== 0 then dirmag
= 1 end
323 dir
.x
=dir
.x
/dirmag
;dir
.y
=dir
.y
/dirmag
;dir
.z
=dir
.z
/dirmag
;
324 dir
.x
= view
.x
-dir
.x
;dir
.y
= view
.y
-dir
.y
;dir
.z
= view
.z
-dir
.z
;
325 dist
= math
.sqrt(dir
.x
*dir
.x
+dir
.y
*dir
.y
+dir
.z
*dir
.z
);
326 if dist
<distmin
then distmin
= dist
; key
= i
end
329 local pos2
= positions
[key
].pos
330 teleports
.do_teleporting(pos
, pos2
, player
:get_player_name(), 3.0)
333 teleports
.lastplayername
= ""