Refactor code to make more vars changable
[minetest_teleports_wuzzy.git] / init.lua
blobd8e76fe2ba5ab6947bc36cb0f1458a795800ba09
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
7 local S
8 if minetest.get_translator then
9 S = minetest.get_translator("teleports")
10 else
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")]
20 else
21 return matched
22 end
23 end)
24 end
26 S = function(textdomain)
27 return function(str, ...)
28 return translate(textdomain or "", str, ...)
29 end
30 end
31 end
34 local F = minetest.formspec_escape
36 teleports = {}
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
44 return
45 end
46 local file, err = io.open(self.filename, "w")
47 if err then
48 return
49 end
50 file:write(datastring)
51 file:close()
52 end
53 function teleports:load()
54 local file, err = io.open(self.filename, "r")
55 if err then
56 self.teleports = {}
57 return
58 end
59 self.teleports = minetest.deserialize(file:read("*all"))
60 if type(self.teleports) ~= "table" then
61 self.teleports = {}
62 end
63 file:close()
64 end
65 function teleports:find_nearby(pos, count)
66 local nearby = {}
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
72 break
73 end
74 end
75 end
76 return nearby
77 end
78 function teleports.animate(pos, playername)
79 minetest.add_particlespawner({
80 amount = 80,
81 time = 5,
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},
88 minexptime = 1,
89 maxexptime = 1,
90 minsize = 0.5,
91 maxsize = 2,
92 collisiondetection = false,
93 vertical = true,
94 texture = "default_diamond.png",
95 playername = playername,
97 minetest.add_particlespawner({
98 amount = 20,
99 time = 5,
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},
106 minexptime = 1,
107 maxexptime = 1,
108 minsize = 0.5,
109 maxsize = 2,
110 collisiondetection = false,
111 vertical = true,
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})
125 else
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)
132 if not delay then
133 teleports.teleportate(pos1, pos2, playername)
134 else
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)
143 local buttons = "";
144 local x = 1
145 local y = 1
146 for i, EachTeleport in ipairs( shown_teleports ) do
147 if i == 4 then
148 x = x + 3.5
149 y = 1
151 if EachTeleport["name"] then
152 buttons = buttons.."button_exit["..x..","..y..";3.5,0.5;tp"..i..";"..F(S("GO>@1",EachTeleport.name)).."]";
153 else
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)).."]";
156 y = y + 1
159 local label
160 if buttons == "" then
161 label = S("No teleport in range!")
162 else
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;]"
169 ..buttons
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
197 return 99
198 else
199 return 0
202 teleports.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
203 return 0
206 teleports:load()
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"})
233 if sign_pos then
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})
238 else
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)
249 teleports:save()
251 end,
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)
261 teleports:save()
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)
269 end,
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"
289 then
290 stack = ItemStack("teleports:teleport")
292 local ret = minetest.item_place(stack, placer, pointed_thing)
293 if ret==nil then
294 return itemstack
295 else
296 return ItemStack("default:diamondblock "..itemstack:get_count()-(1-ret:get_count()))
298 end,
301 minetest.register_abm({
302 nodenames = {"teleports:teleport"},
303 interval = 3,
304 chance = 1,
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)
315 if #positions>0 then
316 local key = math.random(1, #positions)
317 local dir, dirmag;
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)
332 else
333 teleports.lastplayername = ""
336 end,