diff options
Diffstat (limited to 'travelnet/init.lua')
-rw-r--r-- | travelnet/init.lua | 208 |
1 files changed, 132 insertions, 76 deletions
diff --git a/travelnet/init.lua b/travelnet/init.lua index 7827a70..b1ceb6b 100644 --- a/travelnet/init.lua +++ b/travelnet/init.lua @@ -1,5 +1,5 @@ - + --[[ Teleporter networks that allow players to choose a destination out of a list Copyright (C) 2013 Sokomine @@ -17,11 +17,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. - Version: 2.2 (with optional abm for self-healing) - + Version: 2.3 (click button to dig) + Please configure this mod in config.lua Changelog: + 10.03.19 - Added the extra config buttons for locked_travelnet mod. + 09.03.19 - Several PRs merged (sound added, locale changed etc.) + Version bumped to 2.3 + 26.02.19 - Removing a travelnet can now be done by clicking on a button (no need to + wield a diamond pick anymore) + 26.02.19 - Added compatibility with MineClone2 22.09.18 - Move up/move down no longer close the formspec. 22.09.18 - If in creative mode, wield a diamond pick to dig the station. This avoids conflicts with too fast punches. @@ -78,57 +84,65 @@ - target list is now centered if there are less than 9 targets --]] +-- Required to save the travelnet data properly in all cases +if not minetest.safe_file_write then + error("[Mod travelnet] Your Minetest version is no longer supported. (version < 0.4.17)") +end travelnet = {}; travelnet.targets = {}; +travelnet.path = minetest.get_modpath(minetest.get_current_modname()) --- Boilerplate to support localized strings if intllib mod is installed. -if minetest.get_modpath( "intllib" ) and intllib then - travelnet.S = intllib.Getter() -else - travelnet.S = function(s) return s end -end -local S = travelnet.S; +-- Intllib +local S = dofile(travelnet.path .. "/intllib.lua") +travelnet.S = S + minetest.register_privilege("travelnet_attach", { description = S("allows to attach travelnet boxes to travelnets of other players"), give_to_singleplayer = false}); minetest.register_privilege("travelnet_remove", { description = S("allows to dig travelnet boxes which belog to nets of other players"), give_to_singleplayer = false}); -- read the configuration -dofile(minetest.get_modpath("travelnet").."/config.lua"); -- the normal, default travelnet - +dofile(travelnet.path.."/config.lua"); -- the normal, default travelnet +travelnet.mod_data_path = minetest.get_worldpath().."/mod_travelnet.data" -- TODO: save and restore ought to be library functions and not implemented in each individual mod! -- called whenever a station is added or removed travelnet.save_data = function() - + local data = minetest.serialize( travelnet.targets ); - local path = minetest.get_worldpath().."/mod_travelnet.data"; - local file = io.open( path, "w" ); - if( file ) then - file:write( data ); - file:close(); - else - print(S("[Mod travelnet] Error: Savefile '%s' could not be written."):format(tostring(path))); + local success = minetest.safe_file_write( travelnet.mod_data_path, data ); + if( not success ) then + print(S("[Mod travelnet] Error: Savefile '%s' could not be written.") + :format(travelnet.mod_data_path)); end end travelnet.restore_data = function() - - local path = minetest.get_worldpath().."/mod_travelnet.data"; - local file = io.open( path, "r" ); - if( file ) then - local data = file:read("*all"); - travelnet.targets = minetest.deserialize( data ); - file:close(); - else - print(S("[Mod travelnet] Error: Savefile '%s' not found."):format(tostring(path))); + local file = io.open( travelnet.mod_data_path, "r" ); + if( not file ) then + print(S("[Mod travelnet] Error: Savefile '%s' not found.") + :format(travelnet.mod_data_path)); + return; end + + local data = file:read("*all"); + travelnet.targets = minetest.deserialize( data ); + + if( not travelnet.targets ) then + local backup_file = travelnet.mod_data_path..".bak" + print(S("[Mod travelnet] Error: Savefile '%s' is damaged. Saved the backup as '%s'.") + :format(travelnet.mod_data_path, backup_file)); + + minetest.safe_file_write( backup_file, data ); + travelnet.targets = {}; + end + file:close(); end @@ -191,6 +205,9 @@ end travelnet.form_input_handler = function( player, formname, fields) if(formname == "travelnet:show" and fields and fields.pos2str) then local pos = minetest.string_to_pos( fields.pos2str ); + if( locks and (fields.locks_config or fields.locks_authorize)) then + return locks:lock_handle_input( pos, formname, fields, player ) + end -- back button leads back to the main menu if( fields.back and fields.back ~= "" ) then return travelnet.show_current_formspec( pos, @@ -231,7 +248,7 @@ travelnet.reset_formspec = function( meta ) "field[0.3,2.8;9,0.9;station_network;"..S("Assign to Network:")..";".. minetest.formspec_escape(station_network or "").."]".. - "label[0.3,3.1;"..S("You can have more than one network. If unsure, use \"%s\""):format(tostring(station_network)).."\".]".. + "label[0.3,3.1;"..S("You can have more than one network. If unsure, use \"%s\""):format(tostring(station_network))..".]".. "field[0.3,4.4;9,0.9;owner;"..S("Owned by:")..";]".. "label[0.3,4.7;"..S("Unless you know what you are doing, leave this empty.").."]".. "button_exit[1.3,5.3;1.7,0.7;station_help_setup;"..S("Help").."]".. @@ -248,7 +265,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) if( this_node ~= nil and this_node.name == 'travelnet:elevator' ) then is_elevator = true; - end + end if( not( meta )) then return; @@ -258,7 +275,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) local station_name = meta:get_string( "station_name" ); local station_network = meta:get_string( "station_network" ); - if( not( owner_name ) + if( not( owner_name ) or not( station_name ) or station_network == '' or not( station_network )) then @@ -288,7 +305,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) if( not( travelnet.targets[ owner_name ] )) then travelnet.targets[ owner_name ] = {}; end - + -- first station on this network? if( not( travelnet.targets[ owner_name ][ station_network ] )) then travelnet.targets[ owner_name ][ station_network ] = {}; @@ -312,8 +329,10 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) -- add name of station + network + owner + update-button local zusatzstr = ""; local trheight = "10"; - if( this_node and this_node.name=="locked_travelnet:travelnet" ) then - zusatzstr = "field[0.3,11;6,0.7;locks_sent_lock_command;"..S("Locked travelnet. Type /help for help:")..";]"; + if( this_node and this_node.name=="locked_travelnet:travelnet" and locks) then + zusatzstr = "field[0.3,11;6,0.7;locks_sent_lock_command;"..S("Locked travelnet. Type /help for help:")..";]".. + locks.get_authorize_button(10,"10.5").. + locks.get_config_button(11,"10.5") trheight = "11.5"; end local formspec = "size[12,"..trheight.."]".. @@ -331,15 +350,15 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) -- collect all station names in a table local stations = {}; - + for k,v in pairs( travelnet.targets[ owner_name ][ station_network ] ) do table.insert( stations, k ); end -- minetest.chat_send_player(puncher_name, "stations: "..minetest.serialize( stations )); - + local ground_level = 1; if( is_elevator ) then - table.sort( stations, function(a,b) return travelnet.targets[ owner_name ][ station_network ][ a ].pos.y > + table.sort( stations, function(a,b) return travelnet.targets[ owner_name ][ station_network ][ a ].pos.y > travelnet.targets[ owner_name ][ station_network ][ b ].pos.y end); -- find ground level local vgl_timestamp = 999999999999; @@ -348,7 +367,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) travelnet.targets[ owner_name ][ station_network ][ k ].timestamp = os.time(); end if( travelnet.targets[ owner_name ][ station_network ][ k ].timestamp < vgl_timestamp ) then - vgl_timestamp = travelnet.targets[ owner_name ][ station_network ][ k ].timestamp; + vgl_timestamp = travelnet.targets[ owner_name ][ station_network ][ k ].timestamp; ground_level = index; end end @@ -359,10 +378,10 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) travelnet.targets[ owner_name ][ station_network ][ k ].nr = tostring( ground_level - index ); end end - - else + + else -- sort the table according to the timestamp (=time the station was configured) - table.sort( stations, function(a,b) return travelnet.targets[ owner_name ][ station_network ][ a ].timestamp < + table.sort( stations, function(a,b) return travelnet.targets[ owner_name ][ station_network ][ a ].timestamp < travelnet.targets[ owner_name ][ station_network ][ b ].timestamp end); end @@ -400,9 +419,9 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) else -- swap the actual data by which the stations are sorted local old_timestamp = travelnet.targets[ owner_name ][ station_network ][ stations[swap_with_pos]].timestamp; - travelnet.targets[ owner_name ][ station_network ][ stations[swap_with_pos]].timestamp = + travelnet.targets[ owner_name ][ station_network ][ stations[swap_with_pos]].timestamp = travelnet.targets[ owner_name ][ station_network ][ stations[current_pos ]].timestamp; - travelnet.targets[ owner_name ][ station_network ][ stations[current_pos ]].timestamp = + travelnet.targets[ owner_name ][ station_network ][ stations[current_pos ]].timestamp = old_timestamp; -- for elevators, only the "G"(round) marking is moved; no point in swapping stations @@ -423,7 +442,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) x = 4; end - for index,k in ipairs( stations ) do + for index,k in ipairs( stations ) do -- check if there is an elevator door in front that needs to be opened local open_door_cmd = false; @@ -431,7 +450,7 @@ travelnet.update_formspec = function( pos, puncher_name, fields ) open_door_cmd = true; end - if( k ~= station_name or open_door_cmd) then + if( k ~= station_name or open_door_cmd) then i = i+1; -- new column @@ -489,7 +508,7 @@ travelnet.add_target = function( station_name, network_name, pos, player_name, m if( this_node.name == 'travelnet:elevator' ) then -- owner_name = '*'; -- the owner name is not relevant here is_elevator = true; - network_name = tostring( pos.x )..','..tostring( pos.z ); + network_name = tostring( pos.x )..','..tostring( pos.z ); if( not( station_name ) or station_name == '' ) then station_name = S('at %s m'):format(tostring( pos.y )); end @@ -531,7 +550,7 @@ travelnet.add_target = function( station_name, network_name, pos, player_name, m if( not( travelnet.targets[ owner_name ] )) then travelnet.targets[ owner_name ] = {}; end - + -- first station on this network? if( not( travelnet.targets[ owner_name ][ network_name ] )) then travelnet.targets[ owner_name ][ network_name ] = {}; @@ -558,7 +577,7 @@ travelnet.add_target = function( station_name, network_name, pos, player_name, m "Please choose a diffrent/new network name."):format(travelnet.MAX_STATIONS_PER_NETWORK)); return; end - + -- add this station travelnet.targets[ owner_name ][ network_name ][ station_name ] = {pos=pos, timestamp=os.time() }; @@ -574,7 +593,7 @@ travelnet.add_target = function( station_name, network_name, pos, player_name, m meta:set_string( "owner", owner_name ); meta:set_int( "timestamp", travelnet.targets[ owner_name ][ network_name ][ station_name ].timestamp); - meta:set_string("formspec", + meta:set_string("formspec", "size[12,10]".. "field[0.3,0.6;6,0.7;station_name;"..S("Station:")..";".. minetest.formspec_escape(meta:get_string("station_name")).."]".. "field[0.3,3.6;6,0.7;station_network;"..S("Network:")..";"..minetest.formspec_escape(meta:get_string("station_network")).."]" ); @@ -592,7 +611,11 @@ end -- allow doors to open travelnet.open_close_door = function( pos, player, mode ) - local this_node = minetest.get_node( pos ); + local this_node = minetest.get_node_or_nil( pos ); + -- give up if the area is *still* not loaded + if( this_node == nil ) then + return + end local pos2 = {x=pos.x,y=pos.y,z=pos.z}; if( this_node.param2 == 0 ) then pos2 = {x=pos.x,y=pos.y,z=(pos.z-1)}; @@ -609,20 +632,22 @@ travelnet.open_close_door = function( pos, player, mode ) -- do not close the elevator door if it is already closed if( mode==1 and ( string.sub( door_node.name, -7 ) == '_closed' -- handle doors that change their facedir - or ( door_node.param2 == this_node.param2 + or ( door_node.param2 == ((this_node.param2 + 2)%4) and door_node.name ~= 'travelnet:elevator_door_glass_open' + and door_node.name ~= 'travelnet:elevator_door_tin_open' and door_node.name ~= 'travelnet:elevator_door_steel_open'))) then return; end -- do not open the doors if they are already open (works only on elevator-doors; not on doors in general) if( mode==2 and ( string.sub( door_node.name, -5 ) == '_open' -- handle doors that change their facedir - or ( door_node.param2 ~= this_node.param2 + or ( door_node.param2 ~= ((this_node.param2 + 2)%4) and door_node.name ~= 'travelnet:elevator_door_glass_closed' + and door_node.name ~= 'travelnet:elevator_door_tin_closed' and door_node.name ~= 'travelnet:elevator_door_steel_closed'))) then return; end - + if( mode==2 ) then minetest.after( 1, minetest.registered_nodes[ door_node.name ].on_rightclick, pos2, door_node, player ); else @@ -645,6 +670,11 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) return; end + -- show special locks buttons if needed + if( locks and (fields.locks_config or fields.locks_authorize)) then + return locks:lock_handle_input( pos, formname, fields, player ) + end + -- show help text if( fields and fields.station_help_setup and fields.station_help_setup ~= "") then -- simulate right-click @@ -667,6 +697,8 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) description = "travelnet box" elseif( node and node.name and node.name == "travelnet:elevator") then description = "elevator" + elseif( node and node.name and node.name == "locked_travelnet:travelnet") then + description = "locked travelnet" else minetest.chat_send_player(name, "Error: Unkown node."); return @@ -704,7 +736,7 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) -- if the box has not been configured yet if( meta:get_string("station_network")=="" ) then - travelnet.add_target( fields.station_name, fields.station_network, pos, name, meta, fields.owner_name ); + travelnet.add_target( fields.station_name, fields.station_network, pos, name, meta, fields.owner ); return; end @@ -730,8 +762,8 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) local station_name = meta:get_string( "station_name" ); local station_network = meta:get_string( "station_network" ); - if( not( owner_name ) - or not( station_name ) + if( not( owner_name ) + or not( station_name ) or not( station_network ) or not( travelnet.targets[ owner_name ] ) or not( travelnet.targets[ owner_name ][ station_network ] )) then @@ -766,7 +798,7 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) end local this_node = minetest.get_node( pos ); - if( this_node ~= nil and this_node.name == 'travelnet:elevator' ) then + if( this_node ~= nil and this_node.name == 'travelnet:elevator' ) then for k,v in pairs( travelnet.targets[ owner_name ][ station_network ] ) do if( travelnet.targets[ owner_name ][ station_network ][ k ].nr --..' ('..tostring( travelnet.targets[ owner_name ][ station_network ][ k ].pos.y )..'m)' == fields.target) then @@ -794,9 +826,13 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) if( travelnet.travelnet_sound_enabled ) then - minetest.sound_play("128590_7037-lq.mp3", {pos = pos, gain = 1.0, max_hear_distance = 10,}) + if ( this_node.name == 'travelnet:elevator' ) then + minetest.sound_play("travelnet_bell", {pos = pos, gain = 0.75, max_hear_distance = 10,}); + else + minetest.sound_play("travelnet_travel", {pos = pos, gain = 0.75, max_hear_distance = 10,}); + end end - if( travelnet.travelnet_effect_enabled ) then + if( travelnet.travelnet_effect_enabled ) then minetest.add_entity( {x=pos.x,y=pos.y+0.5,z=pos.z}, "travelnet:effect"); -- it self-destructs after 20 turns end @@ -807,16 +843,13 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) local target_pos = travelnet.targets[ owner_name ][ station_network ][ fields.target ].pos; player:moveto( target_pos, false); - if( travelnet.travelnet_sound_enabled ) then - minetest.sound_play("travelnet_travel.wav", {pos = target_pos, gain = 1.0, max_hear_distance = 10,}) - end if( travelnet.travelnet_effect_enabled ) then minetest.add_entity( {x=target_pos.x,y=target_pos.y+0.5,z=target_pos.z}, "travelnet:effect"); -- it self-destructs after 20 turns end -- check if the box has at the other end has been removed. - local node2 = minetest.get_node( target_pos ); + local node2 = minetest.get_node_or_nil( target_pos ); if( node2 ~= nil and node2.name ~= 'ignore' and node2.name ~= 'travelnet:travelnet' and node2.name ~= 'travelnet:elevator' and node2.name ~= "locked_travelnet:travelnet" and node2.name ~= "travelnet:travelnet_private") then -- provide information necessary to identify the removed box @@ -828,9 +861,32 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) -- send the player back as there's no receiving travelnet player:moveto( pos, false ); - -- do this only on servers where the function exists - elseif( player.set_look_horizontal ) then + else + travelnet.rotate_player( target_pos, player, 0 ) + end +end +travelnet.rotate_player = function( target_pos, player, tries ) + -- try later when the box is loaded + local node2 = minetest.get_node_or_nil( target_pos ); + if( node2 == nil ) then + if( tries < 30 ) then + minetest.after( 0, travelnet.rotate_player, target_pos, player, tries+1 ) + end + return + end + + -- play sound at the target position as well + if( travelnet.travelnet_sound_enabled ) then + if ( node2.name == 'travelnet:elevator' ) then + minetest.sound_play("travelnet_bell", {pos = target_pos, gain = 0.75, max_hear_distance = 10,}); + else + minetest.sound_play("travelnet_travel", {pos = target_pos, gain = 0.75, max_hear_distance = 10,}); + end + end + + -- do this only on servers where the function exists + if( player.set_look_horizontal ) then -- rotate the player so that he/she can walk straight out of the box local yaw = 0; local param2 = node2.param2; @@ -843,7 +899,7 @@ travelnet.on_receive_fields = function(pos, formname, fields, player) elseif( param2==3 ) then yaw = 270; end - + player:set_look_horizontal( math.rad( yaw )); player:set_look_vertical( math.rad( 0 )); end @@ -865,19 +921,19 @@ travelnet.remove_box = function( pos, oldnode, oldmetadata, digger ) local station_network = oldmetadata.fields[ "station_network" ]; -- station is not known? then just remove it - if( not( owner_name ) - or not( station_name ) - or not( station_network ) + if( not( owner_name ) + or not( station_name ) + or not( station_network ) or not( travelnet.targets[ owner_name ] ) or not( travelnet.targets[ owner_name ][ station_network ] )) then - + minetest.chat_send_player( digger:get_player_name(), S("Error")..": ".. S("Could not find the station that is to be removed.")); return; end travelnet.targets[ owner_name ][ station_network ][ station_name ] = nil; - + -- inform the owner minetest.chat_send_player( owner_name, S("Station '%s'"):format(station_name ).." ".. S("has been REMOVED from the network '%s'."):format(station_network)); @@ -971,17 +1027,17 @@ end if( travelnet.travelnet_enabled ) then - dofile(minetest.get_modpath("travelnet").."/travelnet.lua"); -- the travelnet node definition + dofile(travelnet.path.."/travelnet.lua"); -- the travelnet node definition end if( travelnet.elevator_enabled ) then - dofile(minetest.get_modpath("travelnet").."/elevator.lua"); -- allows up/down transfers only + dofile(travelnet.path.."/elevator.lua"); -- allows up/down transfers only end if( travelnet.doors_enabled ) then - dofile(minetest.get_modpath("travelnet").."/doors.lua"); -- doors that open and close automaticly when the travelnet or elevator is used + dofile(travelnet.path.."/doors.lua"); -- doors that open and close automaticly when the travelnet or elevator is used end if( travelnet.abm_enabled ) then - dofile(minetest.get_modpath("travelnet").."/restore_network_via_abm.lua"); -- restore travelnet data when players pass by broken networks + dofile(travelnet.path.."/restore_network_via_abm.lua"); -- restore travelnet data when players pass by broken networks end -- upon server start, read the savefile |