add jumpdrive (modified)

This commit is contained in:
Sergei Mozhaisky 2019-11-15 20:25:59 +00:00
parent e44bf63e10
commit ca4ab5fe3d
57 changed files with 2716 additions and 0 deletions

34
jumpdrive/.luacheckrc Normal file
View File

@ -0,0 +1,34 @@
unused_args = false
allow_defined_top = true
ignore = {"512"}
globals = {
"jumpdrive",
-- write
"travelnet",
"pipeworks",
"beds"
}
read_globals = {
-- Stdlib
string = {fields = {"split"}},
table = {fields = {"copy", "getn"}},
-- Minetest
"minetest",
"vector", "ItemStack",
"dump", "VoxelArea",
-- Deps
"unified_inventory", "default", "monitoring",
"digilines",
"mesecons",
"technic",
"locator",
"display_api",
"areas",
"ropes"
}

19
jumpdrive/backbone.lua Normal file
View File

@ -0,0 +1,19 @@
minetest.register_node("jumpdrive:backbone", {
description = "Jumpdrive Backbone",
tiles = {"jumpdrive_backbone.png"},
groups = {cracky=3,oddly_breakable_by_hand=3},
sounds = default.node_sound_glass_defaults(),
light_source = 13
})
minetest.register_craft({
output = 'jumpdrive:backbone',
recipe = {
{'technic:composite_plate', 'default:mese_block', 'technic:composite_plate'},
{'technic:stainless_steel_block', 'default:obsidian', 'technic:stainless_steel_block'},
{'technic:composite_plate', 'default:mese_block', 'technic:composite_plate'}
}
})

View File

@ -0,0 +1,19 @@
minetest.register_node("jumpdrive:backbone", {
description = "Jumpdrive Backbone",
tiles = {"jumpdrive_backbone.png"},
groups = {cracky=3,oddly_breakable_by_hand=3},
sounds = default.node_sound_glass_defaults(),
light_source = 13
})
minetest.register_craft({
output = 'jumpdrive:backbone',
recipe = {
{'default:mese_block', 'default:steelblock', 'default:mese_block'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'default:mese_block', 'default:steelblock', 'default:mese_block'}
}
})

23
jumpdrive/blacklist.lua Normal file
View File

@ -0,0 +1,23 @@
if minetest.get_modpath("technic") then
table.insert(jumpdrive.blacklist, "technic:forcefield_emitter_on")
end
if minetest.get_modpath("advtrains") then
table.insert(jumpdrive.blacklist, "advtrains:dtrack_atc_st")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_cr")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_cr_30")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_cr_45")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_cr_60")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_detector_off_st")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_st")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_st_30")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_st_45")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_st_60")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_vst1")
table.insert(jumpdrive.blacklist, "advtrains:dtrack_vst2")
table.insert(jumpdrive.blacklist, "advtrains:platform_high_stonebrick")
table.insert(jumpdrive.blacklist, "advtrains:platform_low_stonebrick")
end
-- TODO bedrock, advtrains tracks

View File

@ -0,0 +1,6 @@
if minetest.get_modpath("technic") then
table.insert(jumpdrive.blacklist, "technic:forcefield_emitter_on")
end
-- TODO bedrock, advtrains tracks

366
jumpdrive/common.lua Normal file
View File

@ -0,0 +1,366 @@
jumpdrive.sanitize_coord = function(coord)
return math.max( math.min( coord, 31000 ), -31000 )
end
-- get pos object from pos
jumpdrive.get_meta_pos = function(pos)
local meta = minetest.get_meta(pos);
return {x=meta:get_int("x"), y=meta:get_int("y"), z=meta:get_int("z")}
end
-- set pos object from pos
jumpdrive.set_meta_pos = function(pos, target)
local meta = minetest.get_meta(pos);
meta:set_int("x", target.x)
meta:set_int("y", target.y)
meta:set_int("z", target.z)
end
-- get offset from meta
jumpdrive.get_radius = function(pos)
local meta = minetest.get_meta(pos);
return meta:get_int("radius")
end
-- calculates the power requirements for a jump
jumpdrive.calculate_power = function(radius, distance, sourcePos, targetPos)
return 10 * distance * radius
end
jumpdrive.simulate_jump = function(pos, player, show_marker)
local targetPos = jumpdrive.get_meta_pos(pos)
if jumpdrive.check_mapgen(pos) then
return false, "Error: mapgen was active in this area, please try again later for your own safety!"
end
local meta = minetest.get_meta(pos)
local radius = jumpdrive.get_radius(pos)
local distance = vector.distance(pos, targetPos)
local playername = meta:get_string("owner")
if player ~= nil then
playername = player:get_player_name()
end
local radius_vector = {x=radius, y=radius, z=radius}
local source_pos1 = vector.subtract(pos, radius_vector)
local source_pos2 = vector.add(pos, radius_vector)
local target_pos1 = vector.subtract(targetPos, radius_vector)
local target_pos2 = vector.add(targetPos, radius_vector)
local x_overlap = (target_pos1.x <= source_pos2.x and target_pos1.x >= source_pos1.x) or
(target_pos2.x <= source_pos2.x and target_pos2.x >= source_pos1.x)
local y_overlap = (target_pos1.y <= source_pos2.y and target_pos1.y >= source_pos1.y) or
(target_pos2.y <= source_pos2.y and target_pos2.y >= source_pos1.y)
local z_overlap = (target_pos1.z <= source_pos2.z and target_pos1.z >= source_pos1.z) or
(target_pos2.z <= source_pos2.z and target_pos2.z >= source_pos1.z)
if x_overlap and y_overlap and z_overlap then
return false, "Error: jump into itself! extend your jump target"
end
-- load chunk
minetest.get_voxel_manip():read_from_map(target_pos1, target_pos2)
if show_marker then
jumpdrive.show_marker(targetPos, radius, "red")
jumpdrive.show_marker(pos, radius, "green")
end
local msg = nil
local success = true
local blacklisted_pos_list = minetest.find_nodes_in_area(source_pos1, source_pos2, jumpdrive.blacklist)
for _, nodepos in ipairs(blacklisted_pos_list) do
return false, "Can't jump node @ " .. minetest.pos_to_string(nodepos)
end
if minetest.find_node_near(targetPos, radius, "vacuum:vacuum", true) then
msg = "Warning: Jump-target is in vacuum!"
end
if minetest.find_node_near(targetPos, radius, "ignore", true) then
return false, "Warning: Jump-target is in uncharted area"
end
if jumpdrive.is_area_protected(source_pos1, source_pos2, playername) then
return false, "Jump-source is protected!"
end
if jumpdrive.is_area_protected(target_pos1, target_pos2, playername) then
return false, "Jump-target is protected!"
end
local is_empty, empty_msg = jumpdrive.is_area_empty(target_pos1, target_pos2)
if not is_empty then
msg = "Jump-target is obstructed (" .. empty_msg .. ")"
success = false
end
-- check preflight conditions
local preflight_result = jumpdrive.preflight_check(pos, targetPos, radius, playername)
if not preflight_result.success then
-- check failed in customization
msg = "Preflight check failed!"
if preflight_result.message then
msg = preflight_result.message
end
success = false
end
local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos)
local powerstorage = meta:get_int("powerstorage")
if powerstorage < power_req then
-- not enough power
msg = "Not enough power: required=" .. math.floor(power_req) .. ", actual: " .. powerstorage .. " EU"
success = false
end
return success, msg
end
-- preflight check, for overriding
jumpdrive.preflight_check = function(source, destination, radius, playername)
return { success=true }
end
-- execute jump
jumpdrive.execute_jump = function(pos, player)
local meta = minetest.get_meta(pos)
local playername = meta:get_string("owner")
if player ~= nil then
playername = player:get_player_name()
end
local radius = jumpdrive.get_radius(pos)
local targetPos = jumpdrive.get_meta_pos(pos)
local distance = vector.distance(pos, targetPos)
local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos)
local radius_vector = {x=radius, y=radius, z=radius}
local source_pos1 = vector.subtract(pos, radius_vector)
local source_pos2 = vector.add(pos, radius_vector)
local target_pos1 = vector.subtract(targetPos, radius_vector)
local target_pos2 = vector.add(targetPos, radius_vector)
local success, msg = jumpdrive.simulate_jump(pos, player, false)
if not success then
minetest.chat_send_player(playername, msg)
return false, msg
end
-- consume power from storage
local powerstorage = meta:get_int("powerstorage")
meta:set_int("powerstorage", powerstorage - power_req)
local t0 = minetest.get_us_time()
minetest.sound_play("jumpdrive_engine", {
pos = pos,
max_hear_distance = 50,
gain = 0.7,
})
-- actual move
jumpdrive.move(source_pos1, source_pos2, target_pos1, target_pos2)
local t1 = minetest.get_us_time()
local time_micros = t1 - t0
local time_millis = math.floor(time_micros / 1000)
minetest.log("action", "[jumpdrive] jump took " .. time_micros .. " us")
minetest.chat_send_player(playername, "Jump executed in " .. time_millis .. " ms")
-- show animation in source
minetest.add_particlespawner({
amount = 200,
time = 2,
minpos = source_pos1,
maxpos = source_pos2,
minvel = {x = -2, y = -2, z = -2},
maxvel = {x = 2, y = 2, z = 2},
minacc = {x = -3, y = -3, z = -3},
maxacc = {x = 3, y = 3, z = 3},
minexptime = 0.1,
maxexptime = 5,
minsize = 1,
maxsize = 1,
texture = "spark.png",
glow = 5,
})
-- show animation in target
minetest.add_particlespawner({
amount = 200,
time = 2,
minpos = target_pos1,
maxpos = target_pos2,
minvel = {x = -2, y = -2, z = -2},
maxvel = {x = 2, y = 2, z = 2},
minacc = {x = -3, y = -3, z = -3},
maxacc = {x = 3, y = 3, z = 3},
minexptime = 0.1,
maxexptime = 5,
minsize = 1,
maxsize = 1,
texture = "spark.png",
glow = 5,
})
return true, time_micros
end
jumpdrive.update_formspec = function(meta, pos)
meta:set_string("formspec", "size[8,10;]" ..
"field[0,1;2,1;x;X;" .. meta:get_int("x") .. "]" ..
"field[2,1;2,1;y;Y;" .. meta:get_int("y") .. "]" ..
"field[4,1;2,1;z;Z;" .. meta:get_int("z") .. "]" ..
"field[6,1;2,1;radius;Radius;" .. meta:get_int("radius") .. "]" ..
"button_exit[0,2;2,1;jump;Jump]" ..
"button_exit[2,2;2,1;show;Show]" ..
"button_exit[4,2;2,1;save;Save]" ..
"button[6,2;2,1;reset;Reset]" ..
"list[context;main;0,3;8,1;]" ..
"button[0,4;4,1;write_book;Write to book]" ..
"button[4,4;4,1;read_book;Read from book]" ..
"list[current_player;main;0,5;8,4;]" ..
-- listring stuff
"listring[]")
end
jumpdrive.write_to_book = function(pos, sender)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if inv:contains_item("main", {name="default:book", count=1}) then
local stack = inv:remove_item("main", {name="default:book", count=1})
local new_stack = ItemStack("default:book_written")
local data = {}
data.owner = sender:get_player_name()
data.title = "Jumpdrive coordinates"
data.description = "Jumpdrive coordiates"
data.text = minetest.serialize(jumpdrive.get_meta_pos(pos))
data.page = 1
data.page_max = 1
new_stack:get_meta():from_table({ fields = data })
if inv:room_for_item("main", new_stack) then
-- put written book back
inv:add_item("main", new_stack)
else
-- put back old stack
inv:add_item("main", stack)
end
end
end
jumpdrive.read_from_book = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local player_name = meta:get_string("owner")
if inv:contains_item("main", {name="default:book_written", count=1}) then
local stack = inv:remove_item("main", {name="default:book_written", count=1})
local stackMeta = stack:get_meta()
local text = stackMeta:get_string("text")
local data = minetest.deserialize(text)
if data == nil then
-- put book back, it may contain other information
inv:add_item("main", stack)
-- alert player
if nil ~= player_name then
minetest.chat_send_player(player_name, "Invalid data")
end
return
end
local x = tonumber(data.x)
local y = tonumber(data.y)
local z = tonumber(data.z)
if x == nil or y == nil or z == nil then
-- put book back, it may contain other information
inv:add_item("main", stack)
-- alert player
if nil ~= player_name then
minetest.chat_send_player(player_name, "Invalid coordinates")
end
return
end
meta:set_int("x", jumpdrive.sanitize_coord(x))
meta:set_int("y", jumpdrive.sanitize_coord(y))
meta:set_int("z", jumpdrive.sanitize_coord(z))
-- put book back
inv:add_item("main", stack)
elseif inv:contains_item("main", {name="missions:wand_position", count=1}) then
local stack = inv:remove_item("main", {name="missions:wand_position", count=1})
local stackMeta = stack:get_meta()
local text = stackMeta:get_string("pos")
local target_pos = minetest.string_to_pos(text)
if nil == target_pos then
-- put wand back, I don't see a way to corrupt a wand atm
inv:add_item("main", stack)
return
end
local x = target_pos.x
local y = target_pos.y
local z = target_pos.z
if x == nil or y == nil or z == nil then
-- put wand back, I don't see a way to corrupt a wand atm
inv:add_item("main", stack)
return
end
meta:set_int("x", jumpdrive.sanitize_coord(x))
meta:set_int("y", jumpdrive.sanitize_coord(y))
meta:set_int("z", jumpdrive.sanitize_coord(z))
-- put wand back
inv:add_item("main", stack)
end
end
jumpdrive.reset_coordinates = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
end

View File

@ -0,0 +1,57 @@
-- stolen from technic / anchor.lua
local function compute_forceload_positions(pos, meta)
local radius = meta:get_int("radius")
local minpos = vector.subtract(pos, vector.new(radius, radius, radius))
local maxpos = vector.add(pos, vector.new(radius, radius, radius))
local minbpos = {}
local maxbpos = {}
for _, coord in ipairs({"x","y","z"}) do
minbpos[coord] = math.floor(minpos[coord] / 16) * 16
maxbpos[coord] = math.floor(maxpos[coord] / 16) * 16
end
local flposes = {}
for x = minbpos.x, maxbpos.x, 16 do
for y = minbpos.y, maxbpos.y, 16 do
for z = minbpos.z, maxbpos.z, 16 do
table.insert(flposes, vector.new(x, y, z))
end
end
end
return flposes
end
local function currently_forceloaded_positions(meta)
local ser = meta:get_string("forceloaded")
return ser == "" and {} or minetest.deserialize(ser)
end
local function forceload_off(meta)
local flposes = currently_forceloaded_positions(meta)
meta:set_string("forceloaded", "")
for _, p in ipairs(flposes) do
minetest.forceload_free_block(p)
end
end
local function forceload_on(pos, meta)
local want_flposes = compute_forceload_positions(pos, meta)
local have_flposes = {}
for _, p in ipairs(want_flposes) do
if minetest.forceload_block(p) then
table.insert(have_flposes, p)
end
end
meta:set_string("forceloaded", #have_flposes == 0 and "" or minetest.serialize(have_flposes))
end
jumpdrive.anchor_compat = function(from, to)
local to_meta = minetest.get_meta(to)
local from_meta = minetest.get_meta(from)
if from_meta:get_int("enabled") ~= 0 then
-- anchor enabled
forceload_off(from_meta)
forceload_on(to, to_meta)
end
end

64
jumpdrive/compat/beds.lua Normal file
View File

@ -0,0 +1,64 @@
local bed_bottoms = {"beds:bed_bottom", "beds:fancy_bed_bottom"}
-- Calculate a bed's middle position (where players would spawn)
local function calc_bed_middle(bed_pos, facedir)
local dir = minetest.facedir_to_dir(facedir)
local bed_middle = {
x = bed_pos.x + dir.x / 2,
y = bed_pos.y,
z = bed_pos.z + dir.z / 2
}
return bed_middle
end
jumpdrive.beds_compat = function(target_pos1, target_pos2, delta_vector)
if beds == nil or
beds.spawn == nil or
beds.save_spawns == nil then
-- Something is wrong. Don't do anything
return
end
-- Look for beds in target area
local beds_list = minetest.find_nodes_in_area(target_pos1, target_pos2, bed_bottoms)
if next(beds_list) ~= nil then
-- We found some beds!
local source_pos1 = vector.subtract(target_pos1, delta_vector)
local source_pos2 = vector.subtract(target_pos2, delta_vector)
-- Look for players with spawn in source area
local affected_players = {}
for name, pos in pairs(beds.spawn) do
-- pos1 and pos2 must already be sorted
if pos.x >= source_pos1.x and pos.x <= source_pos2.x and
pos.y >= source_pos1.y and pos.y <= source_pos2.y and
pos.z >= source_pos1.z and pos.z <= source_pos2.z then
table.insert(affected_players, name)
end
end
if next(affected_players) ~= nil then
-- Some players seem to be affected.
-- Iterate over all beds
for _, pos in pairs(beds_list) do
local facedir = minetest.get_node(pos).param2
local old_middle = calc_bed_middle(vector.subtract(pos, delta_vector), facedir)
for _, name in ipairs(affected_players) do
local spawn = beds.spawn[name]
if spawn.x == old_middle.x and
spawn.y == old_middle.y and
spawn.z == old_middle.z then
---- Player spawn seems to match old bed position; update
beds.spawn[name] = calc_bed_middle(pos, facedir)
minetest.log("action",
"[jumpdrive] Updated bed spawn for player " .. name)
end
end
end
-- Tell beds mod to save the new spawns.
beds.save_spawns()
end
end
end

View File

@ -0,0 +1,71 @@
local MP = minetest.get_modpath("jumpdrive")
local has_travelnet_mod = minetest.get_modpath("travelnet")
local has_technic_mod = minetest.get_modpath("technic")
local has_locator_mod = minetest.get_modpath("locator")
local has_elevator_mod = minetest.get_modpath("elevator")
local has_display_mod = minetest.get_modpath("display_api")
local has_pipeworks_mod = minetest.get_modpath("pipeworks")
local has_beds_mod = minetest.get_modpath("beds")
local has_ropes_mod = minetest.get_modpath("ropes")
dofile(MP.."/compat/travelnet.lua")
dofile(MP.."/compat/locator.lua")
dofile(MP.."/compat/elevator.lua")
dofile(MP.."/compat/signs.lua")
dofile(MP.."/compat/itemframes.lua")
dofile(MP.."/compat/anchor.lua")
dofile(MP.."/compat/telemosaic.lua")
dofile(MP.."/compat/beds.lua")
dofile(MP.."/compat/ropes.lua")
if has_pipeworks_mod then
dofile(MP.."/compat/teleporttube.lua")
end
jumpdrive.node_compat = function(name, source_pos, target_pos)
if (name == "locator:beacon_1" or name == "locator:beacon_2" or name == "locator:beacon_3") and has_locator_mod then
jumpdrive.locator_compat(source_pos, target_pos)
elseif has_technic_mod and name == "technic:admin_anchor" then
jumpdrive.anchor_compat(source_pos, target_pos)
elseif has_pipeworks_mod and string.find(name, "^pipeworks:teleport_tube") then
jumpdrive.teleporttube_compat(source_pos, target_pos)
elseif name == "telemosaic:beacon" or name == "telemosaic:beacon_protected" then
jumpdrive.telemosaic_compat(source_pos, target_pos)
end
end
jumpdrive.commit_node_compat = function()
if has_pipeworks_mod then
jumpdrive.teleporttube_compat_commit()
end
end
jumpdrive.target_region_compat = function(pos1, pos2, delta_vector)
if has_travelnet_mod then
jumpdrive.travelnet_compat(pos1, pos2)
end
if has_elevator_mod then
jumpdrive.elevator_compat(pos1, pos2)
end
if has_display_mod then
jumpdrive.signs_compat(pos1, pos2)
end
if has_beds_mod then
jumpdrive.beds_compat(pos1, pos2, delta_vector)
end
if has_ropes_mod then
jumpdrive.ropes_compat(pos1, pos2, delta_vector)
end
end

View File

@ -0,0 +1,18 @@
jumpdrive.elevator_compat = function(pos1, pos2)
-- find potential elevators
local elevator_motors = minetest.find_nodes_in_area(pos1, pos2, "elevator:motor")
for _,pos in ipairs(elevator_motors) do
-- delegate to compat
local def = minetest.registered_nodes["elevator:motor"]
minetest.log("action", "[jumpdrive] Restoring elevator @ " .. pos.x .. "/" .. pos.y .. "/" .. pos.z)
-- function(pos, placer, itemstack)
def.after_place_node(pos, nil, nil)
end
end

View File

@ -0,0 +1,20 @@
local update_item = function(pos, node)
-- TODO
end
jumpdrive.itemframes_compat = function(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"itemframes:pedestal", "itemframes:frame"})
if nodes then
for _,pos in pairs(nodes) do
minetest.log("action", "[jumpdrive] updating itemframe @ " .. minetest.pos_to_string(pos))
local node = minetest.get_node(pos)
update_item(pos, node)
end
end
end

View File

@ -0,0 +1,8 @@
jumpdrive.locator_compat = function(from, to)
local meta = minetest.get_meta(to)
locator.remove_beacon(from)
locator.update_beacon(to, meta)
end

View File

@ -0,0 +1,86 @@
local rope_nodes = { -- Top, middle, bottom
{"ropes:rope_top", "ropes:rope", "ropes:rope_bottom"}, -- Rope boxes
{"ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom"}, -- Rope ladders
}
jumpdrive.ropes_compat = function(target_pos1, target_pos2, delta_vector)
if ropes == nil or
ropes.destroy_rope == nil then
-- Something is wrong. Don't do anything
return
end
-- Bottom slice of the target area
local target_bottom_pos1 = target_pos1
local target_bottom_pos2 = {
x = target_pos2.x,
y = target_pos1.y,
z = target_pos2.z
}
-- Top slice of the target area
local target_top_pos1 = {
x = target_pos1.x,
y = target_pos2.y,
z = target_pos1.z
}
local target_top_pos2 = target_pos2
-- For every type of rope
for _, rope_type_nodes in ipairs(rope_nodes) do
-- Look for ropes hanging out of the jump area
local ropes_hanging_out = minetest.find_nodes_in_area(
target_bottom_pos1, target_bottom_pos2,
{rope_type_nodes[1], rope_type_nodes[2]})
for _, pos in ipairs(ropes_hanging_out) do
-- Swap with a proper end node, keeping param2
local end_node = minetest.get_node(pos)
minetest.swap_node(pos, {
name=rope_type_nodes[3],
param2=end_node.param2
})
-- Destroy remainder of the rope below the source area
local remainder_pos = {
x = pos.x - delta_vector.x,
y = pos.y - delta_vector.y - 1,
z = pos.z - delta_vector.z
}
ropes.destroy_rope(remainder_pos, rope_type_nodes)
end
-- Look for ropes hanging into the jump area
local ropes_hanging_in = minetest.find_nodes_in_area(
target_top_pos1, target_top_pos2,
rope_type_nodes)
for _, pos in ipairs(ropes_hanging_in) do
-- Probably there is a loose end above the source area
local end_pos = {
x = pos.x - delta_vector.x,
y = pos.y - delta_vector.y + 1,
z = pos.z - delta_vector.z
}
local end_node = minetest.get_node(end_pos)
if end_node and
(end_node.name == rope_type_nodes[1] or
end_node.name == rope_type_nodes[2]) then
-- Swap with a proper end node, keeping param2
minetest.swap_node(end_pos, {
name=rope_type_nodes[3],
param2=end_node.param2
})
end
-- Destroy remainder of the rope in the target area
ropes.destroy_rope(pos, rope_type_nodes)
end
end
end

View File

@ -0,0 +1,13 @@
jumpdrive.signs_compat = function(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"group:display_modpack_node"})
if nodes then
for _,pos in pairs(nodes) do
minetest.log("action", "[jumpdrive] updating display @ " .. minetest.pos_to_string(pos))
display_api.update_entities(pos)
end
end
end

View File

@ -0,0 +1,47 @@
local function unhash_pos(hash)
local pos = {}
local list = string.split(hash, ':')
pos.x = tonumber(list[1])
pos.y = tonumber(list[2])
pos.z = tonumber(list[3])
return pos
end
local function hash_pos(pos)
return math.floor(pos.x + 0.5) .. ':' ..
math.floor(pos.y + 0.5) .. ':' ..
math.floor(pos.z + 0.5)
end
jumpdrive.telemosaic_compat = function(source_pos, target_pos)
-- delegate to compat
minetest.log("action", "[jumpdrive] Trying to rewire telemosaic @ " ..
target_pos.x .. "/" .. target_pos.y .. "/" .. target_pos.z)
local local_meta = minetest.get_meta(target_pos)
local local_hash = local_meta:get_string('telemosaic:dest')
if local_hash ~= nil and local_hash ~= '' then
local local_pos = unhash_pos(local_hash)
minetest.load_area(local_pos)
local node = minetest.get_node(local_pos)
if node.name == "telemosaic:beacon" then
local remote_hash = minetest.get_meta(local_pos):get_string('telemosaic:dest')
if remote_hash == hash_pos(source_pos) then
-- remote beacon points to this beacon, update link
local remote_pos = unhash_pos(remote_hash)
local remote_meta = minetest.get_meta(remote_pos)
minetest.log("action", "[jumpdrive] rewiring telemosaic at " .. minetest.pos_to_string(remote_pos) ..
" to " .. minetest.pos_to_string(target_pos))
remote_meta:set_string("telemosaic:dest", hash_pos(target_pos))
end
end
end
end

View File

@ -0,0 +1,47 @@
if not pipeworks.tptube then
minetest.log("warning", "[jumpdrive] pipeworks teleport patch not applied, tp-tubes don't work as expected!")
end
-- https://gitlab.com/VanessaE/pipeworks/blob/master/teleport_tube.lua
jumpdrive.teleporttube_compat = function(from, to)
if not pipeworks.tptube then
-- only works with the patch from "./patches/pipeworks.patch"
return
end
local from_hash = pipeworks.tptube.hash(from)
local to_hash = pipeworks.tptube.hash(to)
-- swap data
local db = pipeworks.tptube.get_db()
local data = db[from_hash]
if not data then
minetest.log("warning", "[jumpdrive] no tp-tube data found at hash: " ..
from_hash .. " / pos: " .. minetest.pos_to_string(from))
return
end
minetest.log("action", "[jumpdrive] moving tp-tube data from " ..
from_hash .. " to " .. to_hash .. " at pos: " .. minetest.pos_to_string(from))
data.x = to.x
data.y = to.y
data.z = to.z
db[from_hash] = nil
db[to_hash] = data
end
jumpdrive.teleporttube_compat_commit = function()
if not pipeworks.tptube then
-- only works with the patch from "./patches/pipeworks.patch"
return
end
pipeworks.tptube.save_tube_db()
end

View File

@ -0,0 +1,25 @@
jumpdrive.travelnet_compat = function(pos1, pos2)
local pos_list = minetest.find_nodes_in_area(pos1, pos2, {"travelnet:travelnet"})
if pos_list then
for _,pos in pairs(pos_list) do
local meta = minetest.get_meta(pos);
minetest.log("action", "[jumpdrive] Restoring travelnet @ " .. pos.x .. "/" .. pos.y .. "/" .. pos.z)
local owner_name = meta:get_string( "owner" );
local station_name = meta:get_string( "station_name" );
local station_network = meta:get_string( "station_network" );
if (travelnet.targets[owner_name]
and travelnet.targets[owner_name][station_network]
and travelnet.targets[owner_name][station_network][station_name]) then
travelnet.targets[owner_name][station_network][station_name].pos = pos
end
end
if travelnet.save_data ~= nil then
travelnet.save_data()
end
end
end

84
jumpdrive/digiline.lua Normal file
View File

@ -0,0 +1,84 @@
--https://github.com/minetest-mods/technic/blob/master/technic/machines/HV/forcefield.lua
jumpdrive.digiline_effector = function(pos, _, channel, msg)
local set_channel = "jumpdrive" -- static channel for now
local msgt = type(msg)
if msgt ~= "table" then
return
end
if channel ~= set_channel then
return
end
local meta = minetest.get_meta(pos)
local radius = jumpdrive.get_radius(pos)
local targetPos = jumpdrive.get_meta_pos(pos)
local distance = vector.distance(pos, targetPos)
local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos)
if msg.command == "get" then
digilines.receptor_send(pos, digilines.rules.default, set_channel, {
powerstorage = meta:get_int("powerstorage"),
radius = radius,
position = pos,
target = targetPos,
distance = distance,
power_req = power_req
})
elseif msg.command == "reset" then
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
jumpdrive.update_formspec(meta, pos)
elseif msg.command == "set" then
local value = tonumber(msg.value)
if value == nil then
-- not a number
return
end
if msg.key == "x" then
meta:set_int("x", value)
elseif msg.key == "y" then
meta:set_int("y", value)
elseif msg.key == "z" then
meta:set_int("z", value)
elseif msg.key == "radius" then
if value >= 1 and value <= jumpdrive.config.max_radius then
meta:set_int("radius", value)
end
end
elseif msg.command == "simulate" or msg.command == "show" then
local success, resultmsg = jumpdrive.simulate_jump(pos)
digilines.receptor_send(pos, digilines.rules.default, set_channel, {
success=success,
msg=resultmsg
})
elseif msg.command == "jump" then
local success, time = jumpdrive.execute_jump(pos)
local send_pos = pos
if success then
-- send new message in target pos
send_pos = targetPos
end
digilines.receptor_send(send_pos, digilines.rules.default, set_channel, {
success = success,
time = time
})
end
end

178
jumpdrive/engine.lua Normal file
View File

@ -0,0 +1,178 @@
minetest.register_node("jumpdrive:engine", {
description = "Jumpdrive",
tiles = {"jumpdrive.png"},
tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
stack = stack:peek_item(1)
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = {bottom = 1}
},
connects_to = {"group:technic_hv_cable"},
connect_sides = {"bottom", "top", "left", "right", "front", "back"},
light_source = 13,
groups = {cracky=3,oddly_breakable_by_hand=3,tubedevice=1, tubedevice_receiver=1,technic_machine = 1, technic_hv = 1},
sounds = default.node_sound_glass_defaults(),
digiline = {
receptor = {action = function() end},
effector = {
action = jumpdrive.digiline_effector
},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name() or "")
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
meta:set_int("radius", 5)
meta:set_int("powerstorage", 0)
local inv = meta:get_inventory()
inv:set_size("main", 8)
meta:set_int("HV_EU_input", 0)
meta:set_int("HV_EU_demand", 0)
jumpdrive.update_formspec(meta, pos)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local name = player:get_player_name()
return inv:is_empty("main") and not minetest.is_protected(pos, name)
end,
technic_run = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("HV_EU_input")
local demand = meta:get_int("HV_EU_demand")
local store = meta:get_int("powerstorage")
meta:set_string("infotext", "Power: " .. eu_input .. "/" .. demand .. " Store: " .. store)
if store < jumpdrive.config.powerstorage then
-- charge
meta:set_int("HV_EU_demand", jumpdrive.config.powerrequirement)
store = store + eu_input
meta:set_int("powerstorage", math.min(store, jumpdrive.config.powerstorage))
else
-- charged
meta:set_int("HV_EU_demand", 0)
end
end,
on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos);
if not sender then
return
end
if minetest.is_protected(pos, sender:get_player_name()) then
-- not allowed
return
end
if fields.read_book then
jumpdrive.read_from_book(pos)
jumpdrive.update_formspec(meta, pos)
return
end
if fields.reset then
jumpdrive.reset_coordinates(pos)
jumpdrive.update_formspec(meta, pos)
return
end
if fields.write_book then
jumpdrive.write_to_book(pos, sender)
return
end
local x = tonumber(fields.x);
local y = tonumber(fields.y);
local z = tonumber(fields.z);
local radius = tonumber(fields.radius);
if x == nil or y == nil or z == nil or radius == nil or radius < 1 then
return
end
local max_radius = jumpdrive.config.max_radius
if radius > max_radius then
minetest.chat_send_player(sender:get_player_name(), "Invalid jump: max-radius=" .. max_radius)
return
end
-- update coords
meta:set_int("x", jumpdrive.sanitize_coord(x))
meta:set_int("y", jumpdrive.sanitize_coord(y))
meta:set_int("z", jumpdrive.sanitize_coord(z))
meta:set_int("radius", radius)
jumpdrive.update_formspec(meta, pos)
if fields.jump then
jumpdrive.execute_jump(pos, sender)
end
if fields.show then
local success, msg = jumpdrive.simulate_jump(pos, sender, true)
if not success then
minetest.chat_send_player(sender:get_player_name(), msg)
return
end
minetest.chat_send_player(sender:get_player_name(), "Simulation successful")
end
end,
on_punch = function(pos, node, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
local meta = minetest.get_meta(pos);
local radius = meta:get_int("radius")
jumpdrive.show_marker(pos, radius, "green")
end
})
minetest.register_craft({
output = 'jumpdrive:engine',
recipe = {
{'jumpdrive:backbone', 'default:diamondblock', 'jumpdrive:backbone'},
{'default:diamondblock', 'technic:blue_energy_crystal', 'default:diamondblock'},
{'jumpdrive:backbone', 'technic:hv_transformer', 'jumpdrive:backbone'}
}
})
technic.register_machine("HV", "jumpdrive:engine", technic.receiver)

178
jumpdrive/engine.lua.ORIG Normal file
View File

@ -0,0 +1,178 @@
minetest.register_node("jumpdrive:engine", {
description = "Jumpdrive",
tiles = {"jumpdrive.png"},
tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("main", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
stack = stack:peek_item(1)
return inv:room_for_item("main", stack)
end,
input_inventory = "main",
connect_sides = {bottom = 1}
},
connects_to = {"group:technic_hv_cable"},
connect_sides = {"bottom", "top", "left", "right", "front", "back"},
light_source = 13,
groups = {cracky=3,oddly_breakable_by_hand=3,tubedevice=1, tubedevice_receiver=1,technic_machine = 1, technic_hv = 1},
sounds = default.node_sound_glass_defaults(),
digiline = {
receptor = {action = function() end},
effector = {
action = jumpdrive.digiline_effector
},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name() or "")
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
meta:set_int("radius", 5)
meta:set_int("powerstorage", 0)
local inv = meta:get_inventory()
inv:set_size("main", 8)
meta:set_int("HV_EU_input", 0)
meta:set_int("HV_EU_demand", 0)
jumpdrive.update_formspec(meta, pos)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local name = player:get_player_name()
return inv:is_empty("main") and not minetest.is_protected(pos, name)
end,
technic_run = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("HV_EU_input")
local demand = meta:get_int("HV_EU_demand")
local store = meta:get_int("powerstorage")
meta:set_string("infotext", "Power: " .. eu_input .. "/" .. demand .. " Store: " .. store)
if store < jumpdrive.config.powerstorage then
-- charge
meta:set_int("HV_EU_demand", jumpdrive.config.powerrequirement)
store = store + eu_input
meta:set_int("powerstorage", math.min(store, jumpdrive.config.powerstorage))
else
-- charged
meta:set_int("HV_EU_demand", 0)
end
end,
on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos);
if not sender then
return
end
if minetest.is_protected(pos, sender:get_player_name()) then
-- not allowed
return
end
if fields.read_book then
jumpdrive.read_from_book(pos)
jumpdrive.update_formspec(meta, pos)
return
end
if fields.reset then
jumpdrive.reset_coordinates(pos)
jumpdrive.update_formspec(meta, pos)
return
end
if fields.write_book then
jumpdrive.write_to_book(pos, sender)
return
end
local x = tonumber(fields.x);
local y = tonumber(fields.y);
local z = tonumber(fields.z);
local radius = tonumber(fields.radius);
if x == nil or y == nil or z == nil or radius == nil or radius < 1 then
return
end
local max_radius = jumpdrive.config.max_radius
if radius > max_radius then
minetest.chat_send_player(sender:get_player_name(), "Invalid jump: max-radius=" .. max_radius)
return
end
-- update coords
meta:set_int("x", jumpdrive.sanitize_coord(x))
meta:set_int("y", jumpdrive.sanitize_coord(y))
meta:set_int("z", jumpdrive.sanitize_coord(z))
meta:set_int("radius", radius)
jumpdrive.update_formspec(meta, pos)
if fields.jump then
jumpdrive.execute_jump(pos, sender)
end
if fields.show then
local success, msg = jumpdrive.simulate_jump(pos, sender, true)
if not success then
minetest.chat_send_player(sender:get_player_name(), msg)
return
end
minetest.chat_send_player(sender:get_player_name(), "Simulation successful")
end
end,
on_punch = function(pos, node, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
local meta = minetest.get_meta(pos);
local radius = meta:get_int("radius")
jumpdrive.show_marker(pos, radius, "green")
end
})
minetest.register_craft({
output = 'jumpdrive:engine',
recipe = {
{'jumpdrive:backbone', 'default:steelblock', 'jumpdrive:backbone'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'jumpdrive:backbone', 'default:steelblock', 'jumpdrive:backbone'}
}
})
technic.register_machine("HV", "jumpdrive:engine", technic.receiver)

View File

@ -0,0 +1,231 @@
local update_formspec = function(meta, pos)
local active = meta:get_int("active")
local button_line =
"button_exit[0,2;2,1;jump;Jump]" ..
"button_exit[2,2;2,1;show;Show]" ..
"button_exit[4,2;2,1;save;Save]" ..
"button[6,2;2,1;reset;Reset]"
if active == 1 then
local jump_index = meta:get_int("jump_index")
local jump_list = minetest.deserialize( meta:get_string("jump_list") )
meta:set_string("infotext", "Controller active: " .. jump_index .. "/" .. #jump_list)
button_line = "button_exit[0,2;8,1;stop;Stop]"
else
meta:set_string("infotext", "Ready")
end
meta:set_string("formspec", "size[8,10;]" ..
"field[0,1;2,1;x;X;" .. meta:get_int("x") .. "]" ..
"field[3,1;2,1;y;Y;" .. meta:get_int("y") .. "]" ..
"field[6,1;2,1;z;Z;" .. meta:get_int("z") .. "]" ..
button_line ..
"list[context;main;0,3;8,1;]" ..
"button[0,4;4,1;write_book;Write to book]" ..
"button[4,4;4,1;read_book;Read from book]" ..
"list[current_player;main;0,5;8,4;]" ..
-- listring stuff
"listring[]")
end
minetest.register_node("jumpdrive:fleet_controller", {
description = "Jumpdrive Fleet controller",
tiles = {"jumpdrive_fleet_controller_top.png", "jumpdrive_fleet_controller.png"},
groups = {cracky=3,oddly_breakable_by_hand=3},
sounds = default.node_sound_glass_defaults(),
light_source = 13,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
-- jumping active (1=active)
meta:set_int("active", 0)
-- if active, current index in jump list (1...n)
meta:set_int("jump_index", 0)
-- jump list
meta:set_string("jump_list", "")
local inv = meta:get_inventory()
inv:set_size("main", 8)
update_formspec(meta, pos)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local name = player:get_player_name()
return inv:is_empty("main") and not minetest.is_protected(pos, name)
end,
on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos);
if not sender then
return
end
if minetest.is_protected(pos, sender:get_player_name()) then
-- not allowed
return
end
if fields.read_book then
jumpdrive.read_from_book(pos)
update_formspec(meta, pos)
return
end
if fields.reset then
jumpdrive.reset_coordinates(pos)
update_formspec(meta, pos)
return
end
if fields.write_book then
jumpdrive.write_to_book(pos, sender)
return
end
local x = tonumber(fields.x);
local y = tonumber(fields.y);
local z = tonumber(fields.z);
if x == nil or y == nil or z == nil then
return
end
-- update coords
meta:set_int("x", x)
meta:set_int("y", y)
meta:set_int("z", z)
update_formspec(meta, pos)
local t0 = minetest.get_us_time()
local engines_pos_list = jumpdrive.fleet.find_engines(pos)
local t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive-fleet] backbone traversing took " ..
(t1 - t0) .. " us @ " .. minetest.pos_to_string(pos))
local targetPos = {x=meta:get_int("x"),y=meta:get_int("y"),z=meta:get_int("z")}
-- sort by distance, farthes first
jumpdrive.fleet.sort_engines(pos, engines_pos_list)
-- apply new coordinates
jumpdrive.fleet.apply_coordinates(pos, targetPos, engines_pos_list)
if fields.jump then
--TODO check overlapping engines/radius
meta:set_int("active", 1)
meta:set_int("jump_index", 1)
meta:set_string("jump_list", minetest.serialize(engines_pos_list))
update_formspec(meta, pos)
local timer = minetest.get_node_timer(pos)
timer:start(2.0)
end
if fields.stop then
meta:set_int("active", 0)
local timer = minetest.get_node_timer(pos)
timer:stop()
update_formspec(meta, pos)
end
if fields.show then
local playername = sender:get_player_name()
minetest.chat_send_player(playername, "Found " .. #engines_pos_list .. " jumpdrives")
if #engines_pos_list == 0 then
return
end
local index = 1
local async_check
async_check = function()
local engine_pos = engines_pos_list[index]
local success, msg = jumpdrive.simulate_jump(engine_pos, sender, true)
if not success then
minetest.chat_send_player(playername, msg .. " @ " .. minetest.pos_to_string(engine_pos))
return
end
minetest.chat_send_player(sender:get_player_name(), "Check passed for engine " .. index .. "/" .. #engines_pos_list)
if index < #engines_pos_list then
-- more drives to check
index = index + 1
minetest.after(1, async_check)
elseif index >= #engines_pos_list then
-- done
minetest.chat_send_player(sender:get_player_name(), "Simulation successful")
end
end
minetest.after(1, async_check)
end
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local jump_index = meta:get_int("jump_index")
local jump_list = minetest.deserialize( meta:get_string("jump_list") )
if jump_list and jump_index and #jump_list >= jump_index then
local is_last = #jump_list == jump_index
local node_pos = jump_list[jump_index]
local success, msg = jumpdrive.execute_jump(node_pos)
if success then
-- at this point if it is the last engine the metadata does not exist anymore in the current location
if not is_last then
meta:set_int("jump_index", jump_index+1)
update_formspec(meta, pos)
-- re-schedule
local timer = minetest.get_node_timer(pos)
timer:start(2.0)
end
else
meta:set_int("active", 0)
update_formspec(meta, pos)
meta:set_string("infotext", "Engine ".. minetest.pos_to_string(node_pos) .. " failed with: " .. msg)
end
else
meta:set_int("active", 0)
update_formspec(meta, pos)
end
end
})
minetest.register_craft({
output = 'jumpdrive:fleet_controller',
recipe = {
{'jumpdrive:engine', 'jumpdrive:backbone', 'jumpdrive:engine'},
{'jumpdrive:backbone', 'technic:control_logic_unit', 'jumpdrive:backbone'},
{'jumpdrive:engine', 'jumpdrive:backbone', 'jumpdrive:engine'}
}
})

View File

@ -0,0 +1,231 @@
local update_formspec = function(meta, pos)
local active = meta:get_int("active")
local button_line =
"button_exit[0,2;2,1;jump;Jump]" ..
"button_exit[2,2;2,1;show;Show]" ..
"button_exit[4,2;2,1;save;Save]" ..
"button[6,2;2,1;reset;Reset]"
if active == 1 then
local jump_index = meta:get_int("jump_index")
local jump_list = minetest.deserialize( meta:get_string("jump_list") )
meta:set_string("infotext", "Controller active: " .. jump_index .. "/" .. #jump_list)
button_line = "button_exit[0,2;8,1;stop;Stop]"
else
meta:set_string("infotext", "Ready")
end
meta:set_string("formspec", "size[8,10;]" ..
"field[0,1;2,1;x;X;" .. meta:get_int("x") .. "]" ..
"field[3,1;2,1;y;Y;" .. meta:get_int("y") .. "]" ..
"field[6,1;2,1;z;Z;" .. meta:get_int("z") .. "]" ..
button_line ..
"list[context;main;0,3;8,1;]" ..
"button[0,4;4,1;write_book;Write to book]" ..
"button[4,4;4,1;read_book;Read from book]" ..
"list[current_player;main;0,5;8,4;]" ..
-- listring stuff
"listring[]")
end
minetest.register_node("jumpdrive:fleet_controller", {
description = "Jumpdrive Fleet controller",
tiles = {"jumpdrive_fleet_controller.png"},
groups = {cracky=3,oddly_breakable_by_hand=3},
sounds = default.node_sound_glass_defaults(),
light_source = 13,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("x", pos.x)
meta:set_int("y", pos.y)
meta:set_int("z", pos.z)
-- jumping active (1=active)
meta:set_int("active", 0)
-- if active, current index in jump list (1...n)
meta:set_int("jump_index", 0)
-- jump list
meta:set_string("jump_list", "")
local inv = meta:get_inventory()
inv:set_size("main", 8)
update_formspec(meta, pos)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local name = player:get_player_name()
return inv:is_empty("main") and not minetest.is_protected(pos, name)
end,
on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos);
if not sender then
return
end
if minetest.is_protected(pos, sender:get_player_name()) then
-- not allowed
return
end
if fields.read_book then
jumpdrive.read_from_book(pos)
update_formspec(meta, pos)
return
end
if fields.reset then
jumpdrive.reset_coordinates(pos)
update_formspec(meta, pos)
return
end
if fields.write_book then
jumpdrive.write_to_book(pos, sender)
return
end
local x = tonumber(fields.x);
local y = tonumber(fields.y);
local z = tonumber(fields.z);
if x == nil or y == nil or z == nil then
return
end
-- update coords
meta:set_int("x", x)
meta:set_int("y", y)
meta:set_int("z", z)
update_formspec(meta, pos)
local t0 = minetest.get_us_time()
local engines_pos_list = jumpdrive.fleet.find_engines(pos)
local t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive-fleet] backbone traversing took " ..
(t1 - t0) .. " us @ " .. minetest.pos_to_string(pos))
local targetPos = {x=meta:get_int("x"),y=meta:get_int("y"),z=meta:get_int("z")}
-- sort by distance, farthes first
jumpdrive.fleet.sort_engines(pos, engines_pos_list)
-- apply new coordinates
jumpdrive.fleet.apply_coordinates(pos, targetPos, engines_pos_list)
if fields.jump then
--TODO check overlapping engines/radius
meta:set_int("active", 1)
meta:set_int("jump_index", 1)
meta:set_string("jump_list", minetest.serialize(engines_pos_list))
update_formspec(meta, pos)
local timer = minetest.get_node_timer(pos)
timer:start(2.0)
end
if fields.stop then
meta:set_int("active", 0)
local timer = minetest.get_node_timer(pos)
timer:stop()
update_formspec(meta, pos)
end
if fields.show then
local playername = sender:get_player_name()
minetest.chat_send_player(playername, "Found " .. #engines_pos_list .. " jumpdrives")
if #engines_pos_list == 0 then
return
end
local index = 1
local async_check
async_check = function()
local engine_pos = engines_pos_list[index]
local success, msg = jumpdrive.simulate_jump(engine_pos, sender, true)
if not success then
minetest.chat_send_player(playername, msg .. " @ " .. minetest.pos_to_string(engine_pos))
return
end
minetest.chat_send_player(sender:get_player_name(), "Check passed for engine " .. index .. "/" .. #engines_pos_list)
if index < #engines_pos_list then
-- more drives to check
index = index + 1
minetest.after(1, async_check)
elseif index >= #engines_pos_list then
-- done
minetest.chat_send_player(sender:get_player_name(), "Simulation successful")
end
end
minetest.after(1, async_check)
end
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local jump_index = meta:get_int("jump_index")
local jump_list = minetest.deserialize( meta:get_string("jump_list") )
if jump_list and jump_index and #jump_list >= jump_index then
local is_last = #jump_list == jump_index
local node_pos = jump_list[jump_index]
local success, msg = jumpdrive.execute_jump(node_pos)
if success then
-- at this point if it is the last engine the metadata does not exist anymore in the current location
if not is_last then
meta:set_int("jump_index", jump_index+1)
update_formspec(meta, pos)
-- re-schedule
local timer = minetest.get_node_timer(pos)
timer:start(2.0)
end
else
meta:set_int("active", 0)
update_formspec(meta, pos)
meta:set_string("infotext", "Engine ".. minetest.pos_to_string(node_pos) .. " failed with: " .. msg)
end
else
meta:set_int("active", 0)
update_formspec(meta, pos)
end
end
})
minetest.register_craft({
output = 'jumpdrive:fleet_controller',
recipe = {
{'jumpdrive:engine', 'default:steelblock', 'jumpdrive:engine'},
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
{'jumpdrive:engine', 'default:steelblock', 'jumpdrive:engine'}
}
})

View File

@ -0,0 +1,71 @@
jumpdrive.fleet = {}
-- applies the new coordinates derived from the relative position of the controller
jumpdrive.fleet.apply_coordinates = function(controllerPos, targetPos, engine_pos_list)
-- delta between source and target
local delta_vector = vector.subtract(targetPos, controllerPos)
minetest.log("action", "[jumpdrive-fleet] delta-vector: " .. minetest.pos_to_string(delta_vector))
for _,node_pos in pairs(engine_pos_list) do
local new_pos = vector.add(node_pos, delta_vector)
minetest.log("action", "[jumpdrive-fleet] calculated vector: " .. minetest.pos_to_string(new_pos))
jumpdrive.set_meta_pos(node_pos, new_pos)
end
end
-- sort list by distance, farthest first
jumpdrive.fleet.sort_engines = function(pos, engine_pos_list)
table.sort(engine_pos_list, function(a,b)
local a_distance = vector.distance(a, pos)
local b_distance = vector.distance(b, pos)
return a_distance > b_distance
end)
end
-- traverses the backbone and returns a list of engines
jumpdrive.fleet.find_engines = function(pos, visited_hashes, engine_pos_list)
-- minetest.hash_node_position(pos)
visited_hashes = visited_hashes or {}
engine_pos_list = engine_pos_list or {}
local pos1 = vector.subtract(pos, 1)
local pos2 = vector.add(pos,1)
-- load far-away areas
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local engine_nodes = minetest.find_nodes_in_area(pos1, pos2, {"jumpdrive:engine"})
for _,node_pos in pairs(engine_nodes) do
local hash = minetest.hash_node_position(node_pos)
if not visited_hashes[hash] then
-- pos not yet visited
visited_hashes[hash] = true
minetest.log("action", "[jumpdrive-fleet] adding engine @ " .. minetest.pos_to_string(node_pos))
table.insert(engine_pos_list, node_pos)
jumpdrive.fleet.find_engines(node_pos, visited_hashes, engine_pos_list)
end
end
local backbone_nodes = minetest.find_nodes_in_area(pos1, pos2, {"jumpdrive:backbone"})
for _,node_pos in pairs(backbone_nodes) do
local hash = minetest.hash_node_position(node_pos)
if not visited_hashes[hash] then
-- pos not yet visited
visited_hashes[hash] = true
jumpdrive.fleet.find_engines(node_pos, visited_hashes, engine_pos_list)
end
end
return engine_pos_list
end

43
jumpdrive/init.lua Normal file
View File

@ -0,0 +1,43 @@
jumpdrive = {
config = {
-- technic EU storage value
powerstorage = tonumber(minetest.settings:get("jumpdrive.powerstorage")) or 250000,
-- charge value in EU
powerrequirement = tonumber(minetest.settings:get("jumpdrive.power_requirement")) or 2500,
-- allowed radius
max_radius = tonumber(minetest.settings:get("jumpdrive.maxradius")) or 15
},
blacklist = {}
}
local MP = minetest.get_modpath("jumpdrive")
dofile(MP.."/marker.lua")
dofile(MP.."/compat/compat.lua")
dofile(MP.."/is_area_empty.lua")
dofile(MP.."/is_area_protected.lua")
dofile(MP.."/move_objects.lua")
dofile(MP.."/move_metadata.lua")
dofile(MP.."/move_nodetimers.lua")
dofile(MP.."/move.lua")
dofile(MP.."/mapgen.lua")
dofile(MP.."/common.lua")
dofile(MP.."/digiline.lua")
dofile(MP.."/engine.lua")
dofile(MP.."/backbone.lua")
dofile(MP.."/fleet_functions.lua")
dofile(MP.."/fleet_controller.lua")
dofile(MP.."/blacklist.lua")
if minetest.get_modpath("monitoring") then
-- enable metrics
dofile(MP.."/metrics.lua")
end
print("[OK] Jumpdrive")

43
jumpdrive/init.lua.ORIG Normal file
View File

@ -0,0 +1,43 @@
jumpdrive = {
config = {
-- technic EU storage value
powerstorage = tonumber(minetest.settings:get("jumpdrive.powerstorage")) or 1000000,
-- charge value in EU
powerrequirement = tonumber(minetest.settings:get("jumpdrive.power_requirement")) or 2500,
-- allowed radius
max_radius = tonumber(minetest.settings:get("jumpdrive.maxradius")) or 15
},
blacklist = {}
}
local MP = minetest.get_modpath("jumpdrive")
dofile(MP.."/marker.lua")
dofile(MP.."/compat/compat.lua")
dofile(MP.."/is_area_empty.lua")
dofile(MP.."/is_area_protected.lua")
dofile(MP.."/move_objects.lua")
dofile(MP.."/move_metadata.lua")
dofile(MP.."/move_nodetimers.lua")
dofile(MP.."/move.lua")
dofile(MP.."/mapgen.lua")
dofile(MP.."/common.lua")
dofile(MP.."/digiline.lua")
dofile(MP.."/engine.lua")
dofile(MP.."/backbone.lua")
dofile(MP.."/fleet_functions.lua")
dofile(MP.."/fleet_controller.lua")
dofile(MP.."/blacklist.lua")
if minetest.get_modpath("monitoring") then
-- enable metrics
dofile(MP.."/metrics.lua")
end
print("[OK] Jumpdrive")

View File

@ -0,0 +1,47 @@
local c_ignore = minetest.get_content_id("ignore")
local buildable_to_nodes = {}
minetest.after(4, function()
local count = 0
for name, node in pairs(minetest.registered_nodes) do
if node.buildable_to then
count = count + 1
local id = minetest.get_content_id(name)
buildable_to_nodes[id] = true
end
end
minetest.log("action", "[jumpdrive] collected " .. count .. " nodes that are buildable_to")
end)
jumpdrive.is_area_empty = function(pos1, pos2)
local manip = minetest.get_voxel_manip()
local e1, e2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local data = manip:get_data()
for z=pos1.z, pos2.z do
for y=pos1.y, pos2.y do
for x=pos1.x, pos2.x do
local index = area:index(x, y, z)
local id = data[index]
if id == c_ignore then
return false, "Uncharted"
end
if not buildable_to_nodes[id] then
-- not buildable_to
return false, "Occupied"
end
end
end
end
-- only buildable_to nodes found
return true, ""
end

View File

@ -0,0 +1,42 @@
local has_areas_mod = minetest.get_modpath("areas")
local has_protector_mod = minetest.get_modpath("protector")
local protector_radius = (tonumber(minetest.settings:get("protector_radius")) or 30)
jumpdrive.is_area_protected = function(pos1, pos2, playername)
if minetest.is_area_protected then
-- use area protection check
if minetest.is_area_protected(pos1, pos2, playername, 8) then
return true
end
elseif has_protector_mod then
-- use improvised find_nodes check
local radius_vector = {x=protector_radius, y=protector_radius, z=protector_radius}
local protectors = minetest.find_nodes_in_area(
vector.subtract(pos1, radius_vector),
vector.add(pos2, radius_vector),
{"protector:protect", "protector:protect2"}
)
if protectors then
for _,pos in pairs(protectors) do
if minetest.is_protected(pos, playername) then
return true
end
end
end
end
if has_areas_mod then
if not areas:canInteractInArea(pos1, pos2, playername, true) then
-- player can't interact
return true
end
end
-- no protection
return false
end

View File

@ -0,0 +1,42 @@
local has_areas_mod = minetest.get_modpath("areas")
local has_protector_mod = minetest.get_modpath("protector")
local protector_radius = (tonumber(minetest.settings:get("protector_radius")) or 5)
jumpdrive.is_area_protected = function(pos1, pos2, playername)
if minetest.is_area_protected then
-- use area protection check
if minetest.is_area_protected(pos1, pos2, playername, 8) then
return true
end
elseif has_protector_mod then
-- use improvised find_nodes check
local radius_vector = {x=protector_radius, y=protector_radius, z=protector_radius}
local protectors = minetest.find_nodes_in_area(
vector.subtract(pos1, radius_vector),
vector.add(pos2, radius_vector),
{"protector:protect", "protector:protect2"}
)
if protectors then
for _,pos in pairs(protectors) do
if minetest.is_protected(pos, playername) then
return true
end
end
end
end
if has_areas_mod then
if not areas:canInteractInArea(pos1, pos2, playername, true) then
-- player can't interact
return true
end
end
-- no protection
return false
end

14
jumpdrive/license.txt Normal file
View File

@ -0,0 +1,14 @@
Copyright (C) 2018 Thomas Rudin / Buckaroo Banzay
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

63
jumpdrive/mapgen.lua Normal file
View File

@ -0,0 +1,63 @@
local metric_mapgen_events_count
if minetest.get_modpath("monitoring") then
metric_mapgen_events_count = monitoring.gauge(
"jumpdrive_mapgen_events_count",
"number of events in the mapgen cache"
)
end
local events = {} -- list of {minp, maxp, time}
-- update last mapgen event time
minetest.register_on_generated(function(minp, maxp, seed)
table.insert(events, {
minp = minp,
maxp = maxp,
time = minetest.get_us_time()
})
end)
-- cleanup
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < 5 then return end
timer=0
local time = minetest.get_us_time()
local delay_seconds = 20
local copied_events = events
events = {}
local count = 0
for _, event in ipairs(copied_events) do
if event.time > (time - (delay_seconds * 1000000)) then
-- still recent
table.insert(events, event)
count = count + 1
end
end
if metric_mapgen_events_count then
metric_mapgen_events_count.set(count)
end
end)
-- true = mapgen recently active in that area
jumpdrive.check_mapgen = function(pos)
for _, event in ipairs(events) do
if vector.distance(pos, event.minp) < 200 then
return true
end
end
return false
end

54
jumpdrive/marker.lua Normal file
View File

@ -0,0 +1,54 @@
jumpdrive.show_marker = function(pos, radius, color, center_color)
local entity = "jumpdrive:marker_" .. color
local center_entity = "jumpdrive:marker_" .. (center_color or "blue")
minetest.add_entity(pos, center_entity)
minetest.add_entity({x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}, entity)
minetest.add_entity({x=pos.x-radius, y=pos.y+radius, z=pos.z+radius}, entity)
minetest.add_entity({x=pos.x+radius, y=pos.y+radius, z=pos.z-radius}, entity)
minetest.add_entity({x=pos.x-radius, y=pos.y+radius, z=pos.z-radius}, entity)
minetest.add_entity({x=pos.x+radius, y=pos.y-radius, z=pos.z+radius}, entity)
minetest.add_entity({x=pos.x-radius, y=pos.y-radius, z=pos.z+radius}, entity)
minetest.add_entity({x=pos.x+radius, y=pos.y-radius, z=pos.z-radius}, entity)
minetest.add_entity({x=pos.x-radius, y=pos.y-radius, z=pos.z-radius}, entity)
end
local register_marker = function(color)
local texture = "marker_" .. color .. ".png"
minetest.register_entity("jumpdrive:marker_" .. color, {
initial_properties = {
visual = "cube",
visual_size = {x=1.05, y=1.05},
static_save = false,
textures = {
texture,
texture,
texture,
texture,
texture,
texture
},
collisionbox = {-0.525, -0.525, -0.525, 0.525, 0.525, 0.525},
physical = false,
},
on_activate = function(self, staticdata)
minetest.after(8.0, function() self.object:remove() end)
end,
on_rightclick=function(self, clicker)
self.object:remove()
end,
on_punch = function(self, hitter)
self.object:remove()
end,
})
end
register_marker("red")
register_marker("green")
register_marker("blue")

4
jumpdrive/metrics.lua Normal file
View File

@ -0,0 +1,4 @@
local metric = monitoring.counter("jumpdrive_move_calls", "number of jumpdrive.move calls")
local metric_time = monitoring.counter("jumpdrive_move_time", "time usage in microseconds for jumpdrive.move calls")
jumpdrive.move = metric.wrap(metric_time.wrap(jumpdrive.move))

3
jumpdrive/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = jumpdrive
depends = default, technic
optional_depends = mesecons, travelnet, telemosaic, elevator, vacuum, locator, areas, protector, digilines, display_api, pipeworks, monitoring, beds, ropes

145
jumpdrive/move.lua Normal file
View File

@ -0,0 +1,145 @@
local c_air = minetest.get_content_id("air")
-- moves the source to the target area
-- no protection- or overlap checking is done here
jumpdrive.move = function(source_pos1, source_pos2, target_pos1, target_pos2)
minetest.log("action", "[jumpdrive] initiating jump (" ..
minetest.pos_to_string(source_pos1) .. "-" .. minetest.pos_to_string(source_pos2) ..
") (" .. minetest.pos_to_string(target_pos1) .. "-" .. minetest.pos_to_string(target_pos2) .. ")")
-- step 1: copy via voxel manip
-- https://dev.minetest.net/VoxelManip#Examples
-- delta between source and target
local delta_vector = vector.subtract(target_pos1, source_pos1)
-- center of source
local source_center = vector.add(source_pos1, vector.divide(vector.subtract(source_pos2, source_pos1), 2))
minetest.log("action", "[jumpdrive] source-center: " .. minetest.pos_to_string(source_center))
local t0 = minetest.get_us_time()
-- load areas (just a precaution)
if minetest.load_area then
minetest.load_area(source_pos1, source_pos2)
minetest.load_area(target_pos1, target_pos2)
end
-- read source
local manip = minetest.get_voxel_manip()
local e1, e2 = manip:read_from_map(source_pos1, source_pos2)
local source_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local source_data = manip:get_data()
local source_param1 = manip:get_light_data()
local source_param2 = manip:get_param2_data()
minetest.log("action", "[jumpdrive] read source-data")
-- write target
manip = minetest.get_voxel_manip()
e1, e2 = manip:read_from_map(target_pos1, target_pos2)
local target_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local target_data = manip:get_data()
local target_param1 = manip:get_light_data()
local target_param2 = manip:get_param2_data()
minetest.log("action", "[jumpdrive] read target-data");
for z=source_pos1.z, source_pos2.z do
for y=source_pos1.y, source_pos2.y do
for x=source_pos1.x, source_pos2.x do
local source_index = source_area:index(x, y, z)
local target_index = target_area:index(x+delta_vector.x, y+delta_vector.y, z+delta_vector.z)
-- copy block id
target_data[target_index] = source_data[source_index]
-- copy params
target_param1[target_index] = source_param1[source_index]
target_param2[target_index] = source_param2[source_index]
end
end
end
manip:set_data(target_data)
manip:set_light_data(target_param1)
manip:set_param2_data(target_param2)
manip:write_to_map()
manip:update_map()
local t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive] step I took " .. (t1 - t0) .. " us")
-- step 2: check meta/timers and copy if needed
t0 = minetest.get_us_time()
jumpdrive.move_metadata(source_pos1, source_pos2, delta_vector)
jumpdrive.move_nodetimers(source_pos1, source_pos2, delta_vector)
t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive] step II took " .. (t1 - t0) .. " us")
-- step 3: execute target region compat code
t0 = minetest.get_us_time()
jumpdrive.target_region_compat(target_pos1, target_pos2, delta_vector)
t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive] step III took " .. (t1 - t0) .. " us")
-- step 4: move objects
t0 = minetest.get_us_time()
jumpdrive.move_objects(source_center, source_pos1, source_pos2, delta_vector)
-- move players
for _,player in ipairs(minetest.get_connected_players()) do
local playerPos = player:get_pos()
local xMatch = playerPos.x >= (source_pos1.x-0.5) and playerPos.x <= (source_pos2.x+0.5)
local yMatch = playerPos.y >= (source_pos1.y-0.5) and playerPos.y <= (source_pos2.y+0.5)
local zMatch = playerPos.z >= (source_pos1.z-0.5) and playerPos.z <= (source_pos2.z+0.5)
if xMatch and yMatch and zMatch and player:is_player() then
minetest.log("action", "[jumpdrive] moving player: " .. player:get_player_name())
local new_player_pos = vector.add(playerPos, delta_vector)
player:set_pos( new_player_pos );
end
end
t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive] step IV took " .. (t1 - t0) .. " us")
-- step 5: clear source area with voxel manip
t0 = minetest.get_us_time()
manip = minetest.get_voxel_manip()
e1, e2 = manip:read_from_map(source_pos1, source_pos2)
source_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
source_data = manip:get_data()
for z=source_pos1.z, source_pos2.z do
for y=source_pos1.y, source_pos2.y do
for x=source_pos1.x, source_pos2.x do
local source_index = source_area:index(x, y, z)
source_data[source_index] = c_air
end
end
end
manip:set_data(source_data)
manip:write_to_map()
manip:update_map()
t1 = minetest.get_us_time()
minetest.log("action", "[jumpdrive] step V took " .. (t1 - t0) .. " us")
end

View File

@ -0,0 +1,36 @@
-- invoked from move.lua
jumpdrive.move_metadata = function(source_pos1, source_pos2, delta_vector)
local target_pos1 = vector.add(source_pos1, delta_vector)
local target_pos2 = vector.add(source_pos2, delta_vector)
local target_meta_pos_list = minetest.find_nodes_with_meta(target_pos1, target_pos2)
for _,target_pos in pairs(target_meta_pos_list) do
minetest.log("warning", "[jumpdrive] clearing spurious meta in " .. minetest.pos_to_string(target_pos))
local target_meta = minetest.get_meta(target_pos)
target_meta:from_table(nil)
end
local meta_pos_list = minetest.find_nodes_with_meta(source_pos1, source_pos2)
for _,source_pos in pairs(meta_pos_list) do
local target_pos = vector.add(source_pos, delta_vector)
local source_meta = minetest.get_meta(source_pos)
local source_table = source_meta:to_table()
-- copy metadata to target
minetest.get_meta(target_pos):from_table(source_table)
local node = minetest.get_node(source_pos)
jumpdrive.node_compat(node.name, source_pos, target_pos)
-- clear metadata in source
source_meta:from_table(nil)
end
jumpdrive.commit_node_compat()
end

View File

@ -0,0 +1,42 @@
-- collect nodes with on_timer attributes
local node_names_with_timer = {}
minetest.after(4, function()
for _,node in pairs(minetest.registered_nodes) do
if node.on_timer then
table.insert(node_names_with_timer, node.name)
end
end
minetest.log("action", "[jumpdrive] collected " .. #node_names_with_timer .. " items with node timers")
end)
-- invoked from move.lua
jumpdrive.move_nodetimers = function(source_pos1, source_pos2, delta_vector)
if #node_names_with_timer == 0 then
-- no node timer-nodes or not collected yet
return
end
local list = minetest.find_nodes_in_area(source_pos1, source_pos2, node_names_with_timer)
for _,source_pos in pairs(list) do
local target_pos = vector.add(source_pos, delta_vector)
local source_timer = minetest.get_node_timer(source_pos)
local target_timer = minetest.get_node_timer(target_pos)
if source_timer:is_started() then
-- set target timer
target_timer:set(
source_timer:get_timeout(),
source_timer:get_elapsed()
)
-- clear source timer
source_timer:stop()
end
end
end

View File

@ -0,0 +1,39 @@
-- invoked from move.lua
jumpdrive.move_objects = function(source_center, source_pos1, source_pos2, delta_vector)
local all_objects = minetest.get_objects_inside_radius(source_center, 20);
for _,obj in ipairs(all_objects) do
local objPos = obj:get_pos()
local xMatch = objPos.x >= source_pos1.x and objPos.x <= source_pos2.x
local yMatch = objPos.y >= source_pos1.y and objPos.y <= source_pos2.y
local zMatch = objPos.z >= source_pos1.z and objPos.z <= source_pos2.z
local isPlayer = obj:is_player()
if xMatch and yMatch and zMatch and not isPlayer then
minetest.log("action", "[jumpdrive] object: @ " .. minetest.pos_to_string(objPos))
-- coords in range
local entity = obj:get_luaentity()
if not entity then
minetest.log("action", "[jumpdrive] moving object")
obj:set_pos( vector.add(objPos, delta_vector) )
elseif entity.name == "__builtin:item" then
minetest.log("action", "[jumpdrive] moving dropped item")
obj:set_pos( vector.add(objPos, delta_vector) )
else
minetest.log("action", "[jumpdrive] removing entity: " .. entity.name)
obj:remove()
end
end
end
end

View File

@ -0,0 +1,21 @@
diff --git a/teleport_tube.lua b/teleport_tube.lua
index f4bad74..d236f04 100644
--- a/teleport_tube.lua
+++ b/teleport_tube.lua
@@ -50,6 +50,16 @@ local function read_tube_db()
return tp_tube_db
end
+
+-- expose for external batch use (jumpdrive)
+pipeworks.tptube = {
+ hash = hash,
+ save_tube_db = save_tube_db,
+ get_db = function() return tp_tube_db or read_tube_db() end,
+ tp_tube_db_version = tp_tube_db_version
+}
+
+
-- debug formatter for coordinates used below
local fmt = function(pos)
return pos.x..", "..pos.y..", "..pos.z

View File

@ -0,0 +1,24 @@
# Various patches for proper jumpdrive integration
## pipeworks
Teleport tube compat
```
cat ../jumpdrive/patches/pipeworks.patch | patch -p1
```
Adds the following interface:
```lua
-- expose for external batch use (jumpdrive)
pipeworks.tptube = {
hash = hash,
save_tube_db = save_tube_db,
tp_tube_db = tp_tube_db,
tp_tube_db_version = tp_tube_db_version
}
```
Notes:
* `tp_tube_db_version` should be 2.0

148
jumpdrive/readme.md Normal file
View File

@ -0,0 +1,148 @@
Minetest jumpdrive
======
A simple [Jumpdrive](https://en.wikipedia.org/wiki/Jump_drive) for minetest
Take your buildings with you on your journey
* Github: [https://github.com/thomasrudin-mt/jumpdrive](https://github.com/thomasrudin-mt/jumpdrive)
* Forum topic: [https://forum.minetest.net/viewtopic.php?f=9&t=20073](https://forum.minetest.net/viewtopic.php?f=9&t=20073)
# Operation
* Place a 'jumpdrive:engine' into the center of your creation.
* Connect the engine to a technic HV network
* Let the engine charge
* Choose your target coordinates (should be air or ignore blocks)
* Select your cube-radius
* Click "show" and check the green (source) and red (target) destination markers if everything is in range
* Click "jump"
# Compatibility
Optional dependencies:
* Mesecon interaction (execute jump on signal)
* Technic rechargeable (HV)
* Travelnet box (gets rewired after jump)
* Elevator (on_place gets called after jump)
* Locator (gets removed and added after each jump)
* Pipeworks teleport tubes (with a patch to pipeworks)
* Beds (thx to @tuedel)
* Ropes (thx to @tuedel)
* Mission-wand as coordinate bookmark (thx to @SwissalpS)
# Fuel
The engine connects to a technic hv network.
The energy requirements formula looks like this: **10 x radius x distance**
For example:
* Distance: 100 blocks
* Radius: 5 blocks
* Required energy: 10 x 5 x 100 = 5000
# Protection
The source and destination areas are checked for protection so you can't remove and jump into someone else's buildings.
There are currently no checks for plain populated areas (normal terrain) so you can jump happily into the mountains and make swiss cheese :)
A possible solution against this would be a global height or area restriction (see Preflight check).
# Screenshots
Interface:
![](screenshots/screenshot_20180507_200309.png?raw=true)
Example:
![](screenshots/screenshot_20180507_200203.png?raw=true)
# Advanced operation
## Coordinate bookmarking
You can place empty books into the drive inventory and write the coordinates to it with the "Write to book" button
The "Read from book" reads the coordinates from the next book in the inventory
# Settings
Settings in minetest.conf:
* **jumpdrive.maxradius** max radius of the jumpdrive (default: *15*)
* **jumpdrive.powerstorage** power storage of the drive (default: *1000000*)
* **jumpdrive.power_requirement** power requirement for chargin (default: *2500*)
# Lua api
## Preflight check
The preflight check can be overriden to execute additional checks:
```lua
jumpdrive.preflight_check = function(source, destination, radius, player)
-- check for height limit, only space travel allowed
if destination.y < 1000 then
return { success=false, message="Atmospheric travel not allowed!" }
end
-- everything ok
return { success=true }
end
```
## Fuel calc
The default fuel calc can be overwritten by a depending mod:
```lua
-- calculates the power requirements for a jump
jumpdrive.calculate_power = function(radius, distance, sourcePos, targetPos)
return 10 * distance * radius
end
```
# Sources
* jumprive_engine.ogg: https://freesound.org/people/kaboose102/sounds/340257/
# Contributors
* @tuedel
* @SwissalpS
* @Panquesito7
# History
## Next
## 2.0
* various fixes and optimizations
* Fleetcontroller
* Digiline interface
* mod.conf (minetest >= 5.0)
* Beds,ropes,missions compatibility
* calculate_power() override
* overlap check
* No fuel consumption if creative
* Protection checks for source and destination
* preflight check with custom override
* Settings in minetest.conf
* vacuum compatibility (jump into vacuum with air filled vessel)
## 1.1
* improved performance
* Documentation
* Removed complicated cascade function
## 1.0
* Initial version
* Cascade operation (with issues)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@ -50,6 +50,16 @@ local function read_tube_db()
return tp_tube_db
end
-- expose for external batch use (jumpdrive)
pipeworks.tptube = {
hash = hash,
save_tube_db = save_tube_db,
get_db = function() return tp_tube_db or read_tube_db() end,
tp_tube_db_version = tp_tube_db_version
}
-- debug formatter for coordinates used below
local fmt = function(pos)
return pos.x..", "..pos.y..", "..pos.z