├── .github └── workflows │ ├── luacheck.yml │ └── test.yml ├── .luacheckrc ├── backbone.lua ├── blacklist.lua ├── bookmark.lua ├── common.lua ├── compat ├── anchor.lua ├── areas.lua ├── beds.lua ├── compat.lua ├── drawers.lua ├── elevator.lua ├── locator.lua ├── ropes.lua ├── sethome.lua ├── signs.lua ├── technic_networks.lua ├── telemosaic.lua ├── teleporttube.lua ├── textline.lua └── travelnet.lua ├── crafts.lua ├── digiline.lua ├── doc └── digiline.md ├── engine.lua ├── fleet ├── fleet_controller.lua ├── fleet_digiline.lua ├── fleet_formspec.lua └── fleet_functions.lua ├── formspec.lua ├── fuel.lua ├── hooks.lua ├── infotext.lua ├── init.lua ├── is_area_empty.lua ├── is_area_protected.lua ├── jump.lua ├── license.txt ├── mapgen.lua ├── metrics.lua ├── migrate.lua ├── mod.conf ├── move ├── move.lua ├── move_mapdata.lua ├── move_metadata.lua ├── move_nodetimers.lua ├── move_objects.lua └── move_players.lua ├── mtt.lua ├── readme.md ├── screenshots ├── screenshot_20180507_200203.png ├── screenshot_20180507_200309.png └── screenshot_20220305_161502.png ├── settingtypes.txt ├── sounds └── jumpdrive_engine.ogg ├── technic_run.lua ├── testship.we ├── textures ├── jumpdrive.png ├── jumpdrive_air.png ├── jumpdrive_area.png ├── jumpdrive_backbone.png ├── jumpdrive_blue_mese_crystal.png ├── jumpdrive_fleet_controller.png ├── jumpdrive_raw_blue_mese_crystal.png ├── jumpdrive_remote.png ├── jumpdrive_warpdevice.png ├── marker_blue.png ├── marker_green.png ├── marker_red.png └── spark.png ├── upgrade.lua └── warp_device.lua /.github/workflows/luacheck.yml: -------------------------------------------------------------------------------- 1 | name: luacheck 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: apt 13 | run: sudo apt-get install -y luarocks 14 | - name: luacheck install 15 | run: luarocks install --local luacheck 16 | - name: luacheck run 17 | run: $HOME/.luarocks/bin/luacheck ./ 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: buckaroobanzay/mtt@main 12 | with: 13 | modname: jumpdrive 14 | git_dependencies: | 15 | https://github.com/minetest-mods/areas.git 16 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | 2 | globals = { 3 | "jumpdrive", 4 | 5 | -- write 6 | "travelnet", 7 | "pipeworks", 8 | "beds" 9 | } 10 | 11 | read_globals = { 12 | -- Stdlib 13 | string = {fields = {"split"}}, 14 | table = {fields = {"copy", "getn"}}, 15 | "VoxelManip", 16 | 17 | -- Minetest 18 | "core", 19 | "minetest", 20 | "vector", "ItemStack", 21 | "dump", "VoxelArea", 22 | 23 | -- Deps 24 | "unified_inventory", "default", "monitoring", 25 | "digilines", 26 | "mesecons", 27 | "mesecon", 28 | "technic", 29 | "locator", 30 | "display_api", 31 | "areas", 32 | "ropes", 33 | "sethome", 34 | "drawers", 35 | "player_monoids", 36 | "vizlib", 37 | "mcl_sounds", 38 | "mcl_formspec", 39 | "mtt" 40 | } 41 | -------------------------------------------------------------------------------- /backbone.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | minetest.register_node("jumpdrive:backbone", { 4 | description = "Jumpdrive Backbone", 5 | 6 | tiles = {"jumpdrive_backbone.png"}, 7 | groups = {cracky=3,oddly_breakable_by_hand=3,handy=1,pickaxey=1}, 8 | _mcl_blast_resistance = 2, 9 | _mcl_hardness = 0.9, 10 | sounds = jumpdrive.sounds.node_sound_glass_defaults(), 11 | is_ground_content = false, 12 | light_source = 13 13 | }) 14 | -------------------------------------------------------------------------------- /blacklist.lua: -------------------------------------------------------------------------------- 1 | 2 | if minetest.get_modpath("technic") then 3 | table.insert(jumpdrive.blacklist, "technic:forcefield_emitter_on") 4 | end 5 | 6 | -- TODO bedrock, advtrains tracks 7 | -------------------------------------------------------------------------------- /bookmark.lua: -------------------------------------------------------------------------------- 1 | 2 | local book_item, book_written = "" 3 | 4 | if minetest.get_modpath("default") then 5 | book_item = "default:book" 6 | book_written = "default:book_written" 7 | end 8 | 9 | if minetest.get_modpath("mcl_books") then 10 | book_item = "mcl_books:book" 11 | book_written = "mcl_books:written_book" 12 | end 13 | 14 | jumpdrive.write_to_book = function(pos, sender) 15 | local meta = minetest.get_meta(pos) 16 | local inv = meta:get_inventory() 17 | 18 | if inv:contains_item("main", {name=book_item, count=1}) then 19 | local stack = inv:remove_item("main", {name=book_item, count=1}) 20 | 21 | local new_stack = ItemStack(book_written) 22 | 23 | local data = {} 24 | 25 | data.owner = sender:get_player_name() 26 | data.title = "Jumpdrive coordinates" 27 | data.description = "Jumpdrive coordinates" 28 | data.text = minetest.serialize(jumpdrive.get_meta_pos(pos)) 29 | data.page = 1 30 | data.page_max = 1 31 | 32 | new_stack:get_meta():from_table({ fields = data }) 33 | 34 | if inv:room_for_item("main", new_stack) then 35 | -- put written book back 36 | inv:add_item("main", new_stack) 37 | else 38 | -- put back old stack 39 | inv:add_item("main", stack) 40 | end 41 | 42 | end 43 | 44 | end 45 | 46 | local function has_nil(pos) 47 | if nil == pos 48 | or nil == pos.x 49 | or nil == pos.y 50 | or nil == pos.z 51 | then 52 | return true 53 | end 54 | return false 55 | end 56 | 57 | local function sanitize_and_set_coordinates(meta, pos) 58 | meta:set_int("x", jumpdrive.sanitize_coord(pos.x)) 59 | meta:set_int("y", jumpdrive.sanitize_coord(pos.y)) 60 | meta:set_int("z", jumpdrive.sanitize_coord(pos.z)) 61 | end 62 | 63 | jumpdrive.read_from_book = function(pos) 64 | local meta = minetest.get_meta(pos) 65 | local inv = meta:get_inventory() 66 | local player_name = meta:get_string("owner") 67 | 68 | local inv_size = inv:get_size("main") 69 | local stack 70 | local stack_name 71 | local stack_meta 72 | local text 73 | local target_pos 74 | for i = inv_size, 1, -1 do 75 | stack = inv:get_stack("main", i) 76 | stack_name = stack:get_name() 77 | if book_written == stack_name then 78 | -- remove item from inventory 79 | inv:set_stack("main", i, ItemStack()) 80 | stack_meta = stack:get_meta() 81 | text = stack_meta:get_string("text") 82 | local data = minetest.deserialize(text) 83 | if has_nil(data) then 84 | -- put book back where it was, it may contain other information 85 | inv:set_stack("main", i, stack) 86 | else 87 | 88 | target_pos = { 89 | x = tonumber(data.x), 90 | y = tonumber(data.y), 91 | z = tonumber(data.z) 92 | } 93 | 94 | if has_nil(target_pos) then 95 | -- put book back where it was, it may contain other information 96 | inv:set_stack("main", i, stack) 97 | -- alert player 98 | if nil ~= player_name then 99 | minetest.chat_send_player(player_name, "Invalid coordinates") 100 | end 101 | return 102 | end 103 | sanitize_and_set_coordinates(meta, target_pos) 104 | -- put book back to next free slot 105 | inv:add_item("main", stack) 106 | return 107 | end 108 | 109 | elseif "missions:wand_position" == stack_name then 110 | -- remove item from inventory 111 | inv:set_stack("main", i, ItemStack()) 112 | stack_meta = stack:get_meta() 113 | 114 | text = stack_meta:get_string("pos") 115 | target_pos = minetest.string_to_pos(text) 116 | 117 | if has_nil(target_pos) then 118 | -- put wand back where it was. 119 | -- In singleplayer/creative you can get an invalid position wand 120 | inv:set_stack("main", i, stack) 121 | else 122 | -- don't know how you could get unsanitary coords in a wand, 123 | -- let's just be safe 124 | sanitize_and_set_coordinates(meta, target_pos) 125 | -- put wand back to next free slot 126 | inv:add_item("main", stack) 127 | return 128 | end 129 | 130 | elseif "ccompass:" == stack_name:sub(1, 9) 131 | or "compass:" == stack_name:sub(1, 8) then 132 | -- remove item from inventory 133 | inv:set_stack("main", i, ItemStack()) 134 | stack_meta = stack:get_meta() 135 | 136 | text = stack_meta:get_string("target_pos") 137 | target_pos = minetest.string_to_pos(text) 138 | 139 | if has_nil(target_pos) then 140 | -- put compass back, it is probably not calibrated 141 | -- we put it at same position as we did not actually use it 142 | inv:set_stack("main", i, stack) 143 | else 144 | sanitize_and_set_coordinates(meta, target_pos) 145 | -- put compass back to next free slot 146 | inv:add_item("main", stack) 147 | return 148 | end 149 | end -- switch item type 150 | end -- loop inventory 151 | -- if we got here, there was nothing. 152 | -- should we or should we not message user? 153 | --[[ 154 | if nil ~= player_name then 155 | minetest.chat_send_player(player_name, "No valid bookmark item found.") 156 | end 157 | --]] 158 | end 159 | 160 | -------------------------------------------------------------------------------- /common.lua: -------------------------------------------------------------------------------- 1 | 2 | local c_air = minetest.get_content_id("air") 3 | 4 | function jumpdrive.clear_area(pos1, pos2) 5 | local manip = minetest.get_voxel_manip() 6 | local e1, e2 = manip:read_from_map(pos1, pos2) 7 | local source_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2}) 8 | local source_data = manip:get_data() 9 | 10 | 11 | for z=pos1.z, pos2.z do 12 | for y=pos1.y, pos2.y do 13 | for x=pos1.x, pos2.x do 14 | 15 | local source_index = source_area:index(x, y, z) 16 | source_data[source_index] = c_air 17 | end 18 | end 19 | end 20 | 21 | manip:set_data(source_data) 22 | manip:write_to_map() 23 | 24 | -- remove metadata 25 | local target_meta_pos_list = minetest.find_nodes_with_meta(pos1, pos2) 26 | for _,target_pos in pairs(target_meta_pos_list) do 27 | local target_meta = minetest.get_meta(target_pos) 28 | target_meta:from_table(nil) 29 | end 30 | end 31 | 32 | function jumpdrive.sanitize_coord(coord) 33 | return math.max( math.min( coord, 31000 ), -31000 ) 34 | end 35 | 36 | -- get pos object from pos 37 | function jumpdrive.get_meta_pos(pos) 38 | local meta = minetest.get_meta(pos); 39 | return {x=meta:get_int("x"), y=meta:get_int("y"), z=meta:get_int("z")} 40 | end 41 | 42 | -- set pos object from pos 43 | function jumpdrive.set_meta_pos(pos, target) 44 | local meta = minetest.get_meta(pos); 45 | meta:set_int("x", target.x) 46 | meta:set_int("y", target.y) 47 | meta:set_int("z", target.z) 48 | end 49 | 50 | -- get offset from meta 51 | function jumpdrive.get_radius(pos) 52 | local meta = minetest.get_meta(pos); 53 | return math.max(math.min(meta:get_int("radius"), jumpdrive.config.max_radius), 1) 54 | end 55 | 56 | -- calculates the power requirements for a jump 57 | -- params: radius, distance, sourcePos, targetPos 58 | function jumpdrive.calculate_power(radius, distance) 59 | return 10 * distance * radius 60 | end 61 | 62 | 63 | -- preflight check, for overriding 64 | -- params: source, destination, radius, playername 65 | function jumpdrive.preflight_check() 66 | return { success=true } 67 | end 68 | 69 | function jumpdrive.reset_coordinates(pos) 70 | local meta = minetest.get_meta(pos) 71 | 72 | meta:set_int("x", pos.x) 73 | meta:set_int("y", pos.y) 74 | meta:set_int("z", pos.z) 75 | 76 | end 77 | 78 | function jumpdrive.get_mapblock_from_pos(pos) 79 | return { 80 | x = math.floor(pos.x / 16), 81 | y = math.floor(pos.y / 16), 82 | z = math.floor(pos.z / 16) 83 | } 84 | end 85 | 86 | jumpdrive.digiline_rules = { 87 | -- digilines.rules.default 88 | {x= 1,y= 0,z= 0},{x=-1,y= 0,z= 0}, -- along x beside 89 | {x= 0,y= 0,z= 1},{x= 0,y= 0,z=-1}, -- along z beside 90 | {x= 1,y= 1,z= 0},{x=-1,y= 1,z= 0}, -- 1 node above along x diagonal 91 | {x= 0,y= 1,z= 1},{x= 0,y= 1,z=-1}, -- 1 node above along z diagonal 92 | {x= 1,y=-1,z= 0},{x=-1,y=-1,z= 0}, -- 1 node below along x diagonal 93 | {x= 0,y=-1,z= 1},{x= 0,y=-1,z=-1}, -- 1 node below along z diagonal 94 | -- added rules for digi cable 95 | {x= 0,y= 1,z= 0},{x= 0,y=-1,z= 0}, -- along y above and below 96 | } 97 | -------------------------------------------------------------------------------- /compat/anchor.lua: -------------------------------------------------------------------------------- 1 | -- stolen from technic / anchor.lua 2 | 3 | local function compute_forceload_positions(pos, meta) 4 | local radius = meta:get_int("radius") 5 | local minpos = vector.subtract(pos, vector.new(radius, radius, radius)) 6 | local maxpos = vector.add(pos, vector.new(radius, radius, radius)) 7 | local minbpos = {} 8 | local maxbpos = {} 9 | for _, coord in ipairs({"x","y","z"}) do 10 | minbpos[coord] = math.floor(minpos[coord] / 16) * 16 11 | maxbpos[coord] = math.floor(maxpos[coord] / 16) * 16 12 | end 13 | local flposes = {} 14 | for x = minbpos.x, maxbpos.x, 16 do 15 | for y = minbpos.y, maxbpos.y, 16 do 16 | for z = minbpos.z, maxbpos.z, 16 do 17 | table.insert(flposes, vector.new(x, y, z)) 18 | end 19 | end 20 | end 21 | return flposes 22 | end 23 | 24 | local function currently_forceloaded_positions(meta) 25 | local ser = meta:get_string("forceloaded") 26 | return ser == "" and {} or minetest.deserialize(ser) 27 | end 28 | 29 | local function forceload_off(meta) 30 | local flposes = currently_forceloaded_positions(meta) 31 | meta:set_string("forceloaded", "") 32 | for _, p in ipairs(flposes) do 33 | minetest.forceload_free_block(p) 34 | end 35 | end 36 | 37 | local function forceload_on(pos, meta) 38 | local want_flposes = compute_forceload_positions(pos, meta) 39 | local have_flposes = {} 40 | for _, p in ipairs(want_flposes) do 41 | if minetest.forceload_block(p) then 42 | table.insert(have_flposes, p) 43 | end 44 | end 45 | meta:set_string("forceloaded", #have_flposes == 0 and "" or minetest.serialize(have_flposes)) 46 | end 47 | 48 | minetest.override_item("technic:admin_anchor", { 49 | on_movenode = function(from_pos, to_pos) 50 | local to_meta = minetest.get_meta(to_pos) 51 | local from_meta = minetest.get_meta(from_pos) 52 | 53 | if from_meta:get_int("enabled") ~= 0 then 54 | -- anchor enabled 55 | forceload_off(from_meta) 56 | forceload_on(to_pos, to_meta) 57 | end 58 | end 59 | }) 60 | -------------------------------------------------------------------------------- /compat/areas.lua: -------------------------------------------------------------------------------- 1 | jumpdrive.register_after_jump(function(from_area, to_area) 2 | local delta_vector = vector.subtract(to_area.pos1, from_area.pos1) 3 | local pos1 = from_area.pos1 4 | local pos2 = from_area.pos2 5 | 6 | local list = areas:getAreasIntersectingArea(pos1, pos2) 7 | local dirty = false 8 | 9 | for id, area in pairs(list) do 10 | local xMatch = area.pos1.x >= pos1.x and area.pos2.x <= pos2.x 11 | local yMatch = area.pos1.y >= pos1.y and area.pos2.y <= pos2.y 12 | local zMatch = area.pos1.z >= pos1.z and area.pos2.z <= pos2.z 13 | 14 | if xMatch and yMatch and zMatch then 15 | dirty = true 16 | minetest.log("action", "[jumpdrive] moving area " .. id) 17 | 18 | areas:move( 19 | id, 20 | area, 21 | vector.add(area.pos1, delta_vector), 22 | vector.add(area.pos2, delta_vector) 23 | ) 24 | end 25 | end 26 | 27 | if dirty then 28 | areas:save() 29 | end 30 | end) 31 | -------------------------------------------------------------------------------- /compat/beds.lua: -------------------------------------------------------------------------------- 1 | local bed_bottoms = { "beds:bed_bottom", "beds:fancy_bed_bottom" } 2 | 3 | -- Sanity checks 4 | assert(beds, "global `beds` not found") 5 | assert(beds.spawn, "field `spawn` doesn't exist in `beds`") 6 | assert(beds.save_spawns, "field `save_spawns` doesn't exist in `beds`") 7 | 8 | -- Calculate a bed's middle position (where players would spawn) 9 | local function calc_bed_middle(bed_pos, facedir) 10 | local dir = core.facedir_to_dir(facedir) 11 | local bed_middle = { 12 | x = bed_pos.x + dir.x / 2, 13 | y = bed_pos.y, 14 | z = bed_pos.z + dir.z / 2 15 | } 16 | return bed_middle 17 | end 18 | 19 | local bed_from_positions = {} 20 | 21 | for _, nodename in ipairs(bed_bottoms) do 22 | -- Override bed definitions 23 | core.override_item(nodename, { 24 | on_movenode = function(from_pos) 25 | -- Collect bed positions while jumping 26 | table.insert(bed_from_positions, from_pos) 27 | end 28 | }) 29 | end 30 | 31 | -- Executed after jump 32 | jumpdrive.register_after_jump(function(from_area, to_area) 33 | local delta_vector = vector.subtract(to_area.pos1, from_area.pos1) 34 | local modified = false 35 | 36 | -- Go over all collected bed positions 37 | for _, bed_pos in ipairs(bed_from_positions) do 38 | local facedir = core.get_node(bed_pos).param2 39 | local sleep_pos = calc_bed_middle(bed_pos, facedir) 40 | -- Sleep position in target area 41 | local new_sleep_pos = vector.add(sleep_pos, delta_vector) 42 | local sleep_pos_floor = vector.floor(sleep_pos) 43 | 44 | for player_name, player_pos in pairs(beds.spawn) do 45 | if vector.equals(sleep_pos_floor, vector.floor(player_pos)) then 46 | -- Player sleeps here, move position 47 | beds.spawn[player_name] = new_sleep_pos 48 | core.log("action", 49 | "[jumpdrive] Updated bed spawn for player " .. player_name) 50 | 51 | -- Set modified flag to save afterwards 52 | modified = true 53 | end 54 | end 55 | end 56 | 57 | if modified then 58 | -- Tell beds mod to save the new spawns. 59 | beds.save_spawns() 60 | end 61 | 62 | -- Clear collected bed positions 63 | bed_from_positions = {} 64 | end) 65 | -------------------------------------------------------------------------------- /compat/compat.lua: -------------------------------------------------------------------------------- 1 | local MP = minetest.get_modpath("jumpdrive") 2 | 3 | local has_technic_mod = minetest.get_modpath("technic") 4 | local has_locator_mod = minetest.get_modpath("locator") 5 | local has_display_mod = minetest.get_modpath("display_api") 6 | local has_pipeworks_mod = minetest.get_modpath("pipeworks") 7 | local has_beds_mod = minetest.get_modpath("beds") 8 | local has_ropes_mod = minetest.get_modpath("ropes") 9 | local has_sethome_mod = minetest.get_modpath("sethome") 10 | local has_areas_mod = minetest.get_modpath("areas") 11 | local has_drawers_mod = minetest.get_modpath("drawers") 12 | local has_textline_mod = minetest.get_modpath("textline") 13 | 14 | if minetest.get_modpath("travelnet") then 15 | dofile(MP.."/compat/travelnet.lua") 16 | end 17 | 18 | if minetest.get_modpath("elevator") then 19 | dofile(MP.."/compat/elevator.lua") 20 | end 21 | 22 | if has_technic_mod then 23 | dofile(MP.."/compat/anchor.lua") 24 | dofile(MP.."/compat/technic_networks.lua") 25 | end 26 | 27 | if has_locator_mod then 28 | dofile(MP.."/compat/locator.lua") 29 | end 30 | 31 | if has_drawers_mod then 32 | dofile(MP.."/compat/drawers.lua") 33 | end 34 | 35 | if has_display_mod then 36 | dofile(MP.."/compat/signs.lua") 37 | end 38 | 39 | if has_textline_mod then 40 | dofile(MP.."/compat/textline.lua") 41 | end 42 | 43 | if has_areas_mod then 44 | dofile(MP.."/compat/areas.lua") 45 | end 46 | 47 | if has_sethome_mod then 48 | dofile(MP.."/compat/sethome.lua") 49 | end 50 | 51 | if has_ropes_mod then 52 | dofile(MP.."/compat/ropes.lua") 53 | end 54 | 55 | if has_beds_mod then 56 | dofile(MP.."/compat/beds.lua") 57 | end 58 | 59 | dofile(MP.."/compat/telemosaic.lua") 60 | 61 | if has_pipeworks_mod then 62 | dofile(MP.."/compat/teleporttube.lua") 63 | end 64 | 65 | jumpdrive.node_compat = function(name, source_pos, target_pos, source_pos1, source_pos2, delta_vector) 66 | 67 | if has_pipeworks_mod and string.find(name, "^pipeworks:teleport_tube") then 68 | jumpdrive.teleporttube_compat(source_pos, target_pos) 69 | 70 | elseif name == "telemosaic:beacon" or name == "telemosaic:beacon_err" 71 | or name == "telemosaic:beacon_disabled" or name == "telemosaic:beacon_protected" 72 | or name == "telemosaic:beacon_err_protected" or name == "telemosaic:beacon_disabled_protected" then 73 | jumpdrive.telemosaic_compat(source_pos, target_pos, source_pos1, source_pos2, delta_vector) 74 | 75 | end 76 | end 77 | 78 | jumpdrive.commit_node_compat = function() 79 | -- Nothing to do here 80 | end 81 | -------------------------------------------------------------------------------- /compat/drawers.lua: -------------------------------------------------------------------------------- 1 | 2 | assert(type(drawers.spawn_visuals) == "function") 3 | 4 | -- refresh drawers in new area after jump 5 | minetest.register_on_mods_loaded(function() 6 | for nodename, nodedef in pairs(minetest.registered_nodes) do 7 | if nodedef.groups and nodedef.groups.drawer then 8 | minetest.override_item(nodename, { 9 | on_movenode = function(_, to_pos) 10 | minetest.after(1, function() 11 | drawers.spawn_visuals(to_pos) 12 | end) 13 | end 14 | }) 15 | end 16 | end 17 | end) 18 | -------------------------------------------------------------------------------- /compat/elevator.lua: -------------------------------------------------------------------------------- 1 | 2 | local nodedef = minetest.registered_nodes["elevator:motor"] 3 | 4 | minetest.override_item("elevator:motor", { 5 | on_movenode = function(_, to_pos) 6 | minetest.log("action", "[jumpdrive] Restoring elevator @ " .. to_pos.x .. "/" .. to_pos.y .. "/" .. to_pos.z) 7 | nodedef.after_place_node(to_pos, nil, nil) 8 | end 9 | }) 10 | -------------------------------------------------------------------------------- /compat/locator.lua: -------------------------------------------------------------------------------- 1 | 2 | for i=1,3 do 3 | minetest.override_item("locator:beacon_" .. i, { 4 | on_movenode = function(from_pos, to_pos) 5 | local meta = minetest.get_meta(to_pos) 6 | locator.remove_beacon(from_pos) 7 | locator.update_beacon(to_pos, meta) 8 | end 9 | }) 10 | end 11 | -------------------------------------------------------------------------------- /compat/ropes.lua: -------------------------------------------------------------------------------- 1 | local rope_nodes = { -- Top, middle, bottom 2 | {"ropes:rope_top", "ropes:rope", "ropes:rope_bottom"}, -- Rope boxes 3 | {"ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom"}, -- Rope ladders 4 | } 5 | 6 | jumpdrive.register_after_jump(function(from_area, to_area) 7 | local delta_vector = vector.subtract(to_area.pos1, from_area.pos1) 8 | local target_pos1 = to_area.pos1 9 | local target_pos2 = to_area.pos2 10 | 11 | if ropes == nil or 12 | ropes.destroy_rope == nil then 13 | -- Something is wrong. Don't do anything 14 | return 15 | end 16 | 17 | -- Bottom slice of the target area 18 | local target_bottom_pos1 = target_pos1 19 | local target_bottom_pos2 = { 20 | x = target_pos2.x, 21 | y = target_pos1.y, 22 | z = target_pos2.z 23 | } 24 | 25 | -- Top slice of the target area 26 | local target_top_pos1 = { 27 | x = target_pos1.x, 28 | y = target_pos2.y, 29 | z = target_pos1.z 30 | } 31 | local target_top_pos2 = target_pos2 32 | 33 | 34 | 35 | -- For every type of rope 36 | for _, rope_type_nodes in ipairs(rope_nodes) do 37 | 38 | -- Look for ropes hanging out of the jump area 39 | local ropes_hanging_out = minetest.find_nodes_in_area( 40 | target_bottom_pos1, target_bottom_pos2, 41 | {rope_type_nodes[1], rope_type_nodes[2]}) 42 | 43 | for _, pos in ipairs(ropes_hanging_out) do 44 | -- Swap with a proper end node, keeping param2 45 | local end_node = minetest.get_node(pos) 46 | minetest.swap_node(pos, { 47 | name=rope_type_nodes[3], 48 | param2=end_node.param2 49 | }) 50 | 51 | -- Destroy remainder of the rope below the source area 52 | local remainder_pos = { 53 | x = pos.x - delta_vector.x, 54 | y = pos.y - delta_vector.y - 1, 55 | z = pos.z - delta_vector.z 56 | } 57 | ropes.destroy_rope(remainder_pos, rope_type_nodes) 58 | end 59 | 60 | 61 | -- Look for ropes hanging into the jump area 62 | local ropes_hanging_in = minetest.find_nodes_in_area( 63 | target_top_pos1, target_top_pos2, 64 | rope_type_nodes) 65 | 66 | for _, pos in ipairs(ropes_hanging_in) do 67 | -- Probably there is a loose end above the source area 68 | local end_pos = { 69 | x = pos.x - delta_vector.x, 70 | y = pos.y - delta_vector.y + 1, 71 | z = pos.z - delta_vector.z 72 | } 73 | local end_node = minetest.get_node(end_pos) 74 | 75 | if end_node and 76 | (end_node.name == rope_type_nodes[1] or 77 | end_node.name == rope_type_nodes[2]) then 78 | 79 | -- Swap with a proper end node, keeping param2 80 | minetest.swap_node(end_pos, { 81 | name=rope_type_nodes[3], 82 | param2=end_node.param2 83 | }) 84 | end 85 | 86 | -- Destroy remainder of the rope in the target area 87 | ropes.destroy_rope(pos, rope_type_nodes) 88 | end 89 | end 90 | end) 91 | -------------------------------------------------------------------------------- /compat/sethome.lua: -------------------------------------------------------------------------------- 1 | 2 | jumpdrive.register_after_jump(function(from_area, to_area) 3 | local delta_vector = vector.subtract(to_area.pos1, from_area.pos1) 4 | local pos1 = from_area.pos1 5 | local pos2 = from_area.pos2 6 | 7 | -- move /home positions of online players 8 | for _,player in ipairs(minetest.get_connected_players()) do 9 | local name = player:get_player_name() 10 | 11 | local home_pos = sethome.get(name) 12 | if home_pos then 13 | local xMatch = home_pos.x >= pos1.x and home_pos.x <= pos2.x 14 | local yMatch = home_pos.y >= pos1.y and home_pos.y <= pos2.y 15 | local zMatch = home_pos.z >= pos1.z and home_pos.z <= pos2.z 16 | 17 | if xMatch and yMatch and zMatch then 18 | local new_pos = vector.add(home_pos, delta_vector) 19 | sethome.set(name, new_pos) 20 | end 21 | end 22 | end 23 | end) 24 | -------------------------------------------------------------------------------- /compat/signs.lua: -------------------------------------------------------------------------------- 1 | assert(type(display_api.update_entities) == "function") 2 | 3 | -- refresh signs in new area after jump 4 | minetest.register_on_mods_loaded(function() 5 | for nodename, nodedef in pairs(minetest.registered_nodes) do 6 | if nodedef.groups and nodedef.groups.display_api then 7 | minetest.override_item(nodename, { 8 | on_movenode = function(_, to_pos) 9 | minetest.after(1, function() 10 | display_api.update_entities(to_pos) 11 | end) 12 | end 13 | }) 14 | end 15 | end 16 | end) 17 | -------------------------------------------------------------------------------- /compat/technic_networks.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Compatibility hacks for technic plus new network system 3 | -- 4 | -- More information: 5 | -- https://github.com/mt-mods/technic/issues/100 6 | -- 7 | -- See also proposal draft to actually move networks instead of rebuilding: 8 | -- https://github.com/mt-mods/jumpdrive/pull/79 9 | -- 10 | 11 | -- Check for technic mod version compatibility 12 | if technic.remove_network and technic.pos2network and technic.machines then 13 | 14 | local function on_movenode(from_pos, to_pos, info) 15 | -- Destroy network caches at source location, inside jump area 16 | local src_net_id = technic.pos2network(from_pos) 17 | if src_net_id then 18 | technic.remove_network(src_net_id) 19 | end 20 | 21 | -- Destroy network caches at target location, outside jump area 22 | local edge = info.edge 23 | for axis, value in pairs(edge) do 24 | if value ~= 0 then 25 | local axis_dir = {x=0,y=0,z=0} 26 | axis_dir[axis] = value 27 | local edge_pos = vector.add(to_pos, axis_dir) 28 | local dst_net_id = technic.pos2network(edge_pos) 29 | if dst_net_id then 30 | technic.remove_network(dst_net_id) 31 | end 32 | end 33 | end 34 | end 35 | 36 | -- Collect groups for registered technic cables 37 | local cable_groups = {} 38 | for tier,_ in pairs(technic.machines) do 39 | cable_groups[("technic_%s_cable"):format(tier:lower())] = 1 40 | end 41 | 42 | local function is_network_node(_, def) 43 | if not def.groups then return end 44 | for group,_ in pairs(cable_groups) do 45 | if def.groups[group] then return true end 46 | end 47 | return def.groups["technic_machine"] 48 | end 49 | 50 | -- Inject on_movenode functionality but only if node does not already implement it 51 | for name, def in pairs(minetest.registered_nodes) do 52 | if not def.on_movenode and is_network_node(name, def) then 53 | minetest.override_item(name, { on_movenode = on_movenode }) 54 | end 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /compat/telemosaic.lua: -------------------------------------------------------------------------------- 1 | 2 | local function unhash_pos(hash) 3 | local pos = {} 4 | local list = string.split(hash, ':') 5 | pos.x = tonumber(list[1]) 6 | pos.y = tonumber(list[2]) 7 | pos.z = tonumber(list[3]) 8 | return pos 9 | end 10 | 11 | local function hash_pos(pos) 12 | return math.floor(pos.x + 0.5) .. ':' .. 13 | math.floor(pos.y + 0.5) .. ':' .. 14 | math.floor(pos.z + 0.5) 15 | end 16 | 17 | local function is_valid_beacon(name) 18 | if name == "telemosaic:beacon" 19 | or name == "telemosaic:beacon_err" 20 | or name == "telemosaic:beacon_disabled" 21 | or name == "telemosaic:beacon_protected" 22 | or name == "telemosaic:beacon_err_protected" 23 | or name == "telemosaic:beacon_disabled_protected" then 24 | return true 25 | end 26 | return false 27 | end 28 | 29 | jumpdrive.telemosaic_compat = function(source_pos, target_pos, source_pos1, source_pos2, delta_vector) 30 | 31 | -- delegate to compat 32 | minetest.log("action", "[jumpdrive] Trying to rewire telemosaic at " .. minetest.pos_to_string(target_pos)) 33 | 34 | local local_meta = minetest.get_meta(target_pos) 35 | local remote_hash = local_meta:get_string('telemosaic:dest') 36 | 37 | if remote_hash ~= nil and remote_hash ~= '' then 38 | local remote_pos = unhash_pos(remote_hash) 39 | 40 | minetest.load_area(remote_pos) 41 | local node = minetest.get_node(remote_pos) 42 | 43 | if not is_valid_beacon(node.name) then 44 | -- no beacon found, check if it was moved 45 | local xMatch = remote_pos.x >= source_pos1.x and remote_pos.x <= source_pos2.x 46 | local yMatch = remote_pos.y >= source_pos1.y and remote_pos.y <= source_pos2.y 47 | local zMatch = remote_pos.z >= source_pos1.z and remote_pos.z <= source_pos2.z 48 | 49 | if not (xMatch and yMatch and zMatch) then 50 | return -- outside of moved area 51 | end 52 | 53 | remote_pos = vector.add(remote_pos, delta_vector) 54 | minetest.load_area(remote_pos) 55 | node = minetest.get_node(remote_pos) 56 | 57 | if not is_valid_beacon(node.name) then 58 | return -- no beacon anywhere 59 | end 60 | end 61 | 62 | local remote_meta = minetest.get_meta(remote_pos) 63 | local remote_dest = remote_meta:get_string('telemosaic:dest') 64 | 65 | if remote_dest == hash_pos(source_pos) then 66 | -- remote beacon points to this beacon, update link 67 | minetest.log("action", "[jumpdrive] rewiring telemosaic at " .. minetest.pos_to_string(remote_pos) .. 68 | " to " .. minetest.pos_to_string(target_pos)) 69 | 70 | remote_meta:set_string("telemosaic:dest", hash_pos(target_pos)) 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /compat/teleporttube.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | if not pipeworks.tptube then 4 | minetest.log("warning", "[jumpdrive] pipeworks teleport patch not applied, tp-tubes don't work as expected!") 5 | end 6 | 7 | local is_compatible = pipeworks.tptube and pipeworks.tptube.remove_tube 8 | if not is_compatible then 9 | minetest.log("warning", "[jumpdrive] tp-tube api not comptible, consider upgrading the pipeworks mod") 10 | end 11 | 12 | function jumpdrive.teleporttube_compat(from, to) 13 | if not is_compatible then 14 | return 15 | end 16 | 17 | local from_hash = pipeworks.tptube.hash(from) 18 | local to_hash = pipeworks.tptube.hash(to) 19 | 20 | -- swap data 21 | local db = pipeworks.tptube.get_db() 22 | local data = db[from_hash] 23 | 24 | if not data then 25 | minetest.log("warning", "[jumpdrive] no tp-tube data found at hash: " .. 26 | from_hash .. " / pos: " .. minetest.pos_to_string(from)) 27 | return 28 | end 29 | 30 | minetest.log("action", "[jumpdrive] moving tp-tube data from " .. 31 | from_hash .. " to " .. to_hash .. " at pos: " .. minetest.pos_to_string(from)) 32 | 33 | data.x = to.x 34 | data.y = to.y 35 | data.z = to.z 36 | 37 | -- remove source-entry 38 | pipeworks.tptube.remove_tube(from) 39 | 40 | -- set target entry 41 | db[to_hash] = data 42 | pipeworks.tptube.save_tube(to_hash) 43 | end 44 | -------------------------------------------------------------------------------- /compat/textline.lua: -------------------------------------------------------------------------------- 1 | 2 | local textline_def = minetest.registered_nodes["textline:lcd"] 3 | assert(textline_def) 4 | 5 | -- refresh textline entities after the jump 6 | minetest.override_item("textline:lcd", { 7 | on_movenode = function(from_pos, to_pos) 8 | local delta_vector = vector.subtract(to_pos, from_pos) 9 | local objects = minetest.get_objects_inside_radius(from_pos, 0.5) 10 | for _,object in ipairs(objects) do 11 | local entity = object:get_luaentity() 12 | if entity and entity.name == "textline:text" then 13 | object:set_pos(vector.add(object:get_pos(), delta_vector)) 14 | end 15 | end 16 | end 17 | }) 18 | -------------------------------------------------------------------------------- /compat/travelnet.lua: -------------------------------------------------------------------------------- 1 | 2 | assert(type(travelnet.get_travelnets) == "function", "old travelnet-api found, please update the travelnet mod") 3 | 4 | minetest.register_on_mods_loaded(function() 5 | for node, def in pairs(minetest.registered_nodes) do 6 | if def.groups and def.groups.travelnet == 1 then 7 | minetest.override_item(node, { 8 | on_movenode = function(_, to_pos) 9 | local meta = minetest.get_meta(to_pos); 10 | minetest.log("action", "[jumpdrive] Restoring travelnet @ " .. to_pos.x .. "/" .. to_pos.y .. "/" .. to_pos.z) 11 | 12 | local owner_name = meta:get_string( "owner" ); 13 | local station_name = meta:get_string( "station_name" ); 14 | local station_network = meta:get_string( "station_network" ); 15 | 16 | local stations = travelnet.get_travelnets(owner_name) 17 | if (stations[station_network] 18 | and stations[station_network][station_name]) then 19 | -- update station with new position 20 | stations[station_network][station_name].pos = to_pos 21 | travelnet.set_travelnets(owner_name, stations) 22 | end 23 | end 24 | }) 25 | end 26 | end 27 | end) 28 | 29 | jumpdrive.register_after_jump(function() 30 | if travelnet.save_data ~= nil then 31 | -- write data back to files 32 | travelnet.save_data() 33 | end 34 | end) 35 | -------------------------------------------------------------------------------- /crafts.lua: -------------------------------------------------------------------------------- 1 | 2 | if minetest.get_modpath("technic") then 3 | -- technic enabled crafts 4 | minetest.register_craft({ 5 | output = 'jumpdrive:engine', 6 | recipe = { 7 | {'jumpdrive:backbone', 'technic:blue_energy_crystal', 'jumpdrive:backbone'}, 8 | {'jumpdrive:warp_device', 'technic:hv_transformer', 'jumpdrive:warp_device'}, 9 | {'technic:copper_coil', 'technic:hv_cable', 'technic:copper_coil'} 10 | } 11 | }) 12 | 13 | minetest.register_craft({ 14 | output = 'jumpdrive:backbone', 15 | recipe = { 16 | {'default:mese', 'default:steelblock', 'default:mese'}, 17 | {'default:steelblock', 'default:steelblock', 'default:steelblock'}, 18 | {'default:mese', 'default:steelblock', 'default:mese'} 19 | } 20 | }) 21 | 22 | minetest.register_craft({ 23 | output = 'jumpdrive:warp_device', 24 | recipe = { 25 | {'technic:composite_plate', 'technic:wrought_iron_dust', 'technic:composite_plate'}, 26 | {'default:mese', 'technic:machine_casing', 'default:mese'}, 27 | {'technic:copper_coil', 'technic:hv_cable', 'technic:copper_coil'} 28 | } 29 | }) 30 | minetest.register_craft({ 31 | output = 'jumpdrive:fleet_controller', 32 | recipe = { 33 | {'technic:carbon_plate', 'mesecons_luacontroller:luacontroller0000', 'technic:control_logic_unit'}, 34 | {'jumpdrive:backbone', 'technic:machine_casing', 'jumpdrive:backbone'}, 35 | {'basic_materials:stainless_steel_wire', 'default:steelblock', 'basic_materials:stainless_steel_wire'} 36 | } 37 | }) 38 | 39 | elseif minetest.get_modpath("mcl_core") then 40 | -- mineclone crafts 41 | minetest.register_craft({ 42 | output = 'jumpdrive:engine', 43 | recipe = { 44 | {'jumpdrive:backbone', 'mcl_core:ironblock', 'jumpdrive:backbone'}, 45 | {'mcl_core:ironblock', 'mcl_core:ironblock', 'mcl_core:ironblock'}, 46 | {'jumpdrive:backbone', 'mcl_core:ironblock', 'jumpdrive:backbone'} 47 | } 48 | }) 49 | 50 | minetest.register_craft({ 51 | output = 'jumpdrive:backbone', 52 | recipe = { 53 | {'mesecons_torch:redstoneblock', 'mcl_core:ironblock', 'mesecons_torch:redstoneblock'}, 54 | {'mcl_core:ironblock', 'mcl_core:ironblock', 'mcl_core:ironblock'}, 55 | {'mesecons_torch:redstoneblock', 'mcl_core:ironblock', 'mesecons_torch:redstoneblock'} 56 | } 57 | }) 58 | 59 | minetest.register_craft({ 60 | output = 'jumpdrive:warp_device', 61 | recipe = { 62 | {'mesecons:wire_00000000_off', 'mcl_core:diamond', 'mesecons:wire_00000000_off'}, 63 | {'mesecons_torch:redstoneblock', 'mcl_core:ironblock', 'mesecons_torch:redstoneblock'}, 64 | {'mesecons:wire_00000000_off', 'mcl_core:diamond', 'mesecons:wire_00000000_off'} 65 | } 66 | }) 67 | 68 | minetest.register_craft({ 69 | output = 'jumpdrive:fleet_controller', 70 | recipe = { 71 | {'jumpdrive:engine', 'mcl_core:ironblock', 'jumpdrive:engine'}, 72 | {'mcl_core:ironblock', 'mcl_core:ironblock', 'mcl_core:ironblock'}, 73 | {'jumpdrive:engine', 'mcl_core:ironblock', 'jumpdrive:engine'} 74 | } 75 | }) 76 | elseif minetest.get_modpath("default") then 77 | -- minetest_game crafts 78 | minetest.register_craft({ 79 | output = 'jumpdrive:engine', 80 | recipe = { 81 | {'jumpdrive:backbone', 'default:steelblock', 'jumpdrive:backbone'}, 82 | {'default:steelblock', 'default:steelblock', 'default:steelblock'}, 83 | {'jumpdrive:backbone', 'default:steelblock', 'jumpdrive:backbone'} 84 | } 85 | }) 86 | 87 | minetest.register_craft({ 88 | output = 'jumpdrive:backbone', 89 | recipe = { 90 | {'default:mese', 'default:steelblock', 'default:mese'}, 91 | {'default:steelblock', 'default:steelblock', 'default:steelblock'}, 92 | {'default:mese', 'default:steelblock', 'default:mese'} 93 | } 94 | }) 95 | 96 | minetest.register_craft({ 97 | output = 'jumpdrive:warp_device', 98 | recipe = { 99 | {'default:mese_crystal', 'default:diamond', 'default:mese_crystal'}, 100 | {'default:mese', 'default:steelblock', 'default:mese'}, 101 | {'default:mese_crystal', 'default:diamond', 'default:mese_crystal'} 102 | } 103 | }) 104 | 105 | minetest.register_craft({ 106 | output = 'jumpdrive:fleet_controller', 107 | recipe = { 108 | {'jumpdrive:engine', 'default:steelblock', 'jumpdrive:engine'}, 109 | {'default:steelblock', 'default:steelblock', 'default:steelblock'}, 110 | {'jumpdrive:engine', 'default:steelblock', 'jumpdrive:engine'} 111 | } 112 | }) 113 | end 114 | -------------------------------------------------------------------------------- /digiline.lua: -------------------------------------------------------------------------------- 1 | 2 | --https://github.com/minetest-mods/technic/blob/master/technic/machines/HV/forcefield.lua 3 | 4 | local is_int = function(value) 5 | return type(value) == 'number' and math.floor(value) == value 6 | end 7 | 8 | jumpdrive.digiline_effector = function(pos, _, channel, msg) 9 | 10 | local msgt = type(msg) 11 | if msgt ~= "table" then 12 | return 13 | end 14 | 15 | local meta = minetest.get_meta(pos) 16 | 17 | local set_channel = meta:get_string("channel") 18 | if set_channel == "" then 19 | -- backward compatibility with old static channel 20 | set_channel = "jumpdrive" 21 | end 22 | 23 | if channel ~= set_channel then 24 | return 25 | end 26 | 27 | local radius = jumpdrive.get_radius(pos) 28 | local targetPos = jumpdrive.get_meta_pos(pos) 29 | 30 | local distance = vector.distance(pos, targetPos) 31 | local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos) 32 | 33 | if msg.command == "get" then 34 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 35 | powerstorage = meta:get_int("powerstorage"), 36 | radius = radius, 37 | position = pos, 38 | target = targetPos, 39 | distance = distance, 40 | power_req = power_req 41 | }) 42 | 43 | elseif msg.command == "reset" then 44 | meta:set_int("x", pos.x) 45 | meta:set_int("y", pos.y) 46 | meta:set_int("z", pos.z) 47 | jumpdrive.update_formspec(meta, pos) 48 | 49 | elseif msg.command == "set" then 50 | 51 | if msg.key and msg.value then 52 | local value = tonumber(msg.value) 53 | 54 | if value == nil then 55 | -- not a number 56 | return 57 | end 58 | -- backward compatibility with old less flexible set command 59 | if msg.key == "x" then 60 | meta:set_int("x", jumpdrive.sanitize_coord(value)) 61 | elseif msg.key == "y" then 62 | meta:set_int("y", jumpdrive.sanitize_coord(value)) 63 | elseif msg.key == "z" then 64 | meta:set_int("z", jumpdrive.sanitize_coord(value)) 65 | elseif msg.key == "radius" then 66 | if value >= 1 and value <= jumpdrive.config.max_radius then 67 | meta:set_int("radius", value) 68 | end 69 | end 70 | else 71 | -- API requires integers for coord values, noop for everything else 72 | if is_int(msg.x) then meta:set_int("x", jumpdrive.sanitize_coord(msg.x)) end 73 | if is_int(msg.y) then meta:set_int("y", jumpdrive.sanitize_coord(msg.y)) end 74 | if is_int(msg.z) then meta:set_int("z", jumpdrive.sanitize_coord(msg.z)) end 75 | if is_int(msg.r) and msg.r <= jumpdrive.config.max_radius then 76 | meta:set_int("radius", msg.r) 77 | end 78 | if msg.formupdate then 79 | jumpdrive.update_formspec(meta, pos) 80 | end 81 | end 82 | 83 | elseif msg.command == "simulate" or msg.command == "show" then 84 | local success, resultmsg = jumpdrive.simulate_jump(pos) 85 | 86 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 87 | success=success, 88 | msg=resultmsg 89 | }) 90 | 91 | elseif msg.command == "jump" then 92 | local success, timeormsg = jumpdrive.execute_jump(pos) 93 | 94 | if success then 95 | -- send new message in target pos 96 | -- defer sending to allow the environment to "settle" first 97 | minetest.after(0.5, function() 98 | digilines.receptor_send(targetPos, jumpdrive.digiline_rules, set_channel, { 99 | success = success, 100 | time = timeormsg 101 | }) 102 | end) 103 | else 104 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 105 | success = success, 106 | msg = timeormsg 107 | }) 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /doc/digiline.md: -------------------------------------------------------------------------------- 1 | # Digilines interface 2 | 3 | Digiline commands if the `digilines` mod is available 4 | 5 | ## Engine 6 | 7 | ### Query current status/coords 8 | 9 | Request coordinates and engine status 10 | 11 | ```lua 12 | -- request 13 | digiline_send("jumpdrive", { 14 | command = "get" 15 | }) 16 | 17 | -- response sent back on the same channel 18 | if event.type == "digiline" and event.channel == "jumpdrive" then 19 | event.msg = { 20 | powerstorage = 1000000, 21 | radius = 10, 22 | position = {x=0, y=0, z=0}, 23 | target = {x=0, y=0, z=0}, 24 | distance = 100, 25 | power_req = 150000 26 | } 27 | end 28 | ``` 29 | 30 | ### Reset target coordinates 31 | 32 | Resets the target coordinates 33 | 34 | ```lua 35 | -- request 36 | digiline_send("jumpdrive", { 37 | command = "reset" 38 | }) 39 | ``` 40 | 41 | ### Set target coordinates 42 | 43 | Set the target coordinates 44 | 45 | ```lua 46 | -- request 47 | digiline_send("jumpdrive", { command = "set", key = "x", value = 1024 }) 48 | digiline_send("jumpdrive", { command = "set", key = "y", value = 1024 }) 49 | digiline_send("jumpdrive", { command = "set", key = "z", value = 2048 }) 50 | digiline_send("jumpdrive", { command = "set", key = "radius", value = 15 }) 51 | ``` 52 | 53 | Alternate way to set coordinates 54 | 55 | ```lua 56 | -- request 57 | digiline_send("jumpdrive", { command = "set", x = 1024, y = 1024, z = 2048, r = 15, formupdate = false }) 58 | ``` 59 | Where 60 | * `x` sets x coordinate 61 | * `y` sets y coordinate 62 | * `z` sets z coordinate 63 | * `r` sets radius 64 | * `formupdate` updates coordinates on formspec 65 | 66 | Every value is optional. `x`, `y`, `z` and `r` must be integers. `formupdate` is truth value. 67 | 68 | ### Simulate jump 69 | 70 | Simulate a jump 71 | 72 | ```lua 73 | -- request 74 | digiline_send("jumpdrive", { 75 | command = "simulate" 76 | }) 77 | 78 | -- response sent back on the same channel 79 | if event.type == "digiline" and event.channel == "jumpdrive" then 80 | event.msg = { 81 | success = false, -- true if successful 82 | msg = "Protected by xyz!" 83 | } 84 | end 85 | ``` 86 | 87 | ### Execute jump 88 | 89 | Execute a jump 90 | 91 | ```lua 92 | -- request 93 | digiline_send("jumpdrive", { 94 | command = "jump" 95 | }) 96 | 97 | -- response sent back on the same channel 98 | if event.type == "digiline" and event.channel == "jumpdrive" then 99 | event.msg = { 100 | success = true, 101 | time = 1234 -- time used in microseconds 102 | } 103 | end 104 | ``` 105 | 106 | ## Fleetcontroller 107 | 108 | Fleetcontroller interface 109 | 110 | ### Query current status/coords 111 | 112 | Request coordinates and status 113 | 114 | ```lua 115 | -- request 116 | digiline_send("fleetcontroller", { 117 | command = "get" 118 | }) 119 | 120 | -- response sent back on the same channel 121 | if event.type == "digiline" and event.channel == "fleetcontroller" then 122 | event.msg = { 123 | active = true, 124 | engines = { 125 | { 126 | power_req = 10000, 127 | powerstorage = 125000, 128 | radius = 10, 129 | position = { x=0, y=0, z=0 }, 130 | target = { x=0, y=0, z=0 }, 131 | distance = 2500, 132 | },{ 133 | -- etc 134 | } 135 | }, 136 | max_power_req = 100000, -- max power of an engine 137 | total_power_req = 12500000, -- total power of all engines 138 | position = { x=0, y=0, z=0 }, 139 | target = { x=0, y=0, z=0 }, 140 | distance = 2500, 141 | } 142 | end 143 | ``` 144 | 145 | 146 | ### Reset coordinates 147 | 148 | Resets the target coordinates 149 | 150 | ```lua 151 | -- request 152 | digiline_send("fleetcontroller", { 153 | command = "reset" 154 | }) 155 | 156 | -- response sent back on the same channel 157 | if event.type == "digiline" and event.channel == "fleetcontroller" then 158 | -- sent if the jump is still in progress 159 | event.msg = { 160 | success = false, 161 | msg = "Operation not completed" 162 | } 163 | end 164 | ``` 165 | 166 | 167 | ### Set coordinates 168 | 169 | Resets the target coordinates 170 | 171 | Where 172 | * `x` sets x coordinate 173 | * `y` sets y coordinate 174 | * `z` sets z coordinate 175 | * `formupdate` updates coordinates on formspec 176 | 177 | Every value is optional. `x`, `y` and `z` must be integers. `formupdate` is truth value. 178 | 179 | ```lua 180 | -- request 181 | digiline_send("fleetcontroller", { 182 | command = "set", 183 | x = 0, 184 | y = 100, 185 | z = 1024, 186 | formupdate = false 187 | }) 188 | 189 | -- response sent back on the same channel 190 | if event.type == "digiline" and event.channel == "fleetcontroller" then 191 | -- sent if the jump is still in progress 192 | event.msg = { 193 | success = false, 194 | msg = "Operation not completed" 195 | } 196 | end 197 | ``` 198 | 199 | Response is only sent when operation fails due to another operation is in progress like simulation or jump. 200 | Error will not be sent for invalid values or missing values because every value is optional. 201 | 202 | ### Simulate jump 203 | 204 | Simulates a jump 205 | 206 | ```lua 207 | -- request 208 | digiline_send("fleetcontroller", { 209 | command = "simulate" 210 | }) 211 | 212 | -- response sent back on the same channel 213 | if event.type == "digiline" and event.channel == "fleetcontroller" then 214 | -- sent if the jump is still in progress 215 | event.msg = { 216 | success = false, 217 | msg = "Operation not completed" 218 | } 219 | 220 | -- sent on abort 221 | event.msg { 222 | success = false, 223 | index = 1, 224 | count = 10, 225 | msg = "simulation aborted" 226 | } 227 | 228 | -- sent if an error occured 229 | event.msg = { 230 | success = false, 231 | pos = { x=0, y=0, z=0 }, 232 | msg = "Protected by xyz!" 233 | } 234 | 235 | -- sent on success 236 | event.msg = { 237 | success = true, 238 | count = 10, 239 | msgs = { 240 | -- possible warning messages for engines like target in vacuum warning 241 | } 242 | } 243 | end 244 | ``` 245 | 246 | 247 | ### Execute jump 248 | 249 | Executes a jump 250 | 251 | ```lua 252 | -- request 253 | digiline_send("fleetcontroller", { 254 | command = "jump" 255 | }) 256 | 257 | -- response sent back on the same channel 258 | if event.type == "digiline" and event.channel == "fleetcontroller" then 259 | -- sent if the jump is still in progress 260 | event.msg = { 261 | success = false, 262 | msg = "Operation not completed" 263 | } 264 | 265 | -- sent on abort 266 | event.msg { 267 | success = false, 268 | index = 1, 269 | count = 10, 270 | msg = "jump aborted" 271 | } 272 | 273 | -- sent if an error occured 274 | event.msg = { 275 | success = false, 276 | count = 1, 277 | msg = "Protected by xyz!", 278 | msgs = { 279 | -- messages 280 | } 281 | } 282 | 283 | -- sent on success 284 | event.msg = { 285 | success = true, 286 | count = 10, 287 | msgs = { 288 | -- messages 289 | }, 290 | time = 1234 -- microseconds used 291 | } 292 | end 293 | ``` 294 | -------------------------------------------------------------------------------- /engine.lua: -------------------------------------------------------------------------------- 1 | local has_technic = minetest.get_modpath("technic") 2 | local has_vizlib = minetest.get_modpath("vizlib") 3 | 4 | minetest.register_node("jumpdrive:engine", { 5 | description = "Jumpdrive", 6 | 7 | tiles = {"jumpdrive.png"}, 8 | 9 | tube = { 10 | insert_object = function(pos, _, stack) 11 | local meta = minetest.get_meta(pos) 12 | local inv = meta:get_inventory() 13 | return inv:add_item("main", stack) 14 | end, 15 | can_insert = function(pos, _, stack) 16 | local meta = minetest.get_meta(pos) 17 | local inv = meta:get_inventory() 18 | stack = stack:peek_item(1) 19 | 20 | return inv:room_for_item("main", stack) 21 | end, 22 | input_inventory = "main", 23 | connect_sides = {bottom = 1} 24 | }, 25 | 26 | connects_to = {"group:technic_hv_cable"}, 27 | connect_sides = {"bottom", "top", "left", "right", "front", "back"}, 28 | 29 | is_ground_content = false, 30 | light_source = 13, 31 | groups = { 32 | cracky = 3, 33 | oddly_breakable_by_hand = 3, 34 | tubedevice = 1, 35 | tubedevice_receiver = 1, 36 | technic_machine = 1, 37 | technic_hv = 1, 38 | handy=1, 39 | pickaxey=1 40 | }, 41 | _mcl_blast_resistance = 2, 42 | _mcl_hardness = 0.9, 43 | 44 | sounds = jumpdrive.sounds.node_sound_glass_defaults(), 45 | 46 | digiline = { 47 | receptor = { 48 | rules = jumpdrive.digiline_rules, 49 | action = function() end 50 | }, 51 | effector = { 52 | rules = jumpdrive.digiline_rules, 53 | action = jumpdrive.digiline_effector 54 | } 55 | }, 56 | 57 | after_place_node = function(pos, placer) 58 | local meta = minetest.get_meta(pos) 59 | meta:set_string("owner", placer:get_player_name() or "") 60 | -- default digiline channel 61 | meta:set_string("channel", "jumpdrive") 62 | end, 63 | 64 | on_construct = function(pos) 65 | local meta = minetest.get_meta(pos) 66 | meta:set_int("x", pos.x) 67 | meta:set_int("y", pos.y) 68 | meta:set_int("z", pos.z) 69 | meta:set_int("radius", 5) 70 | meta:set_int("powerstorage", 0) 71 | jumpdrive.migrate_engine_meta(pos, meta) 72 | 73 | meta:set_int("HV_EU_input", 0) 74 | meta:set_int("HV_EU_demand", 0) 75 | 76 | -- With [vizlib] when showing radius/simulation 77 | --meta:set_int("simulation_expiry", 0) 78 | 79 | jumpdrive.update_formspec(meta, pos) 80 | end, 81 | 82 | can_dig = function(pos,player) 83 | local meta = minetest.get_meta(pos); 84 | local inv = meta:get_inventory() 85 | local name = player:get_player_name() 86 | 87 | return inv:is_empty("main") and 88 | inv:is_empty("upgrade") and 89 | not minetest.is_protected(pos, name) 90 | end, 91 | 92 | on_timer = function(pos) 93 | local meta = minetest.get_meta(pos) 94 | 95 | local store = meta:get_int("powerstorage") 96 | local max_store = meta:get_int("max_powerstorage") 97 | 98 | if store >= max_store then 99 | -- internal store is full 100 | return true 101 | end 102 | 103 | local inv = meta:get_inventory() 104 | for i=1, inv:get_size("main") do 105 | local stack = inv:get_stack("main", i) 106 | if not stack:is_empty() then 107 | local burn_value = jumpdrive.fuel.get_value(stack:get_name()) 108 | if burn_value > 0 then 109 | local store_space = max_store - store 110 | local fuel_value = burn_value * stack:get_count() 111 | if fuel_value > store_space then 112 | stack:set_count(stack:get_count() - math.ceil(store_space / burn_value)) 113 | store = max_store 114 | else 115 | stack:clear() 116 | store = store + fuel_value 117 | end 118 | inv:set_stack("main", i, stack) 119 | end 120 | end 121 | end 122 | meta:set_int("powerstorage", store) 123 | 124 | if not has_technic then 125 | jumpdrive.update_infotext(meta, pos) 126 | end 127 | 128 | -- restart timer 129 | return true 130 | end, 131 | 132 | technic_run = jumpdrive.technic_run, 133 | 134 | on_receive_fields = function(pos, _, fields, sender) 135 | 136 | local meta = minetest.get_meta(pos); 137 | jumpdrive.migrate_engine_meta(pos, meta) 138 | 139 | if not sender then 140 | return 141 | end 142 | 143 | if minetest.is_protected(pos, sender:get_player_name()) then 144 | -- not allowed 145 | return 146 | end 147 | 148 | if fields.read_book then 149 | jumpdrive.read_from_book(pos) 150 | jumpdrive.update_formspec(meta, pos) 151 | return 152 | end 153 | 154 | if fields.reset then 155 | jumpdrive.reset_coordinates(pos) 156 | jumpdrive.update_formspec(meta, pos) 157 | return 158 | end 159 | 160 | if fields.write_book then 161 | jumpdrive.write_to_book(pos, sender) 162 | return 163 | end 164 | 165 | if fields.set_digiline_channel and fields.digiline_channel then 166 | meta:set_string("channel", fields.digiline_channel) 167 | jumpdrive.update_formspec(meta, pos) 168 | return 169 | end 170 | 171 | local x = tonumber(fields.x); 172 | local y = tonumber(fields.y); 173 | local z = tonumber(fields.z); 174 | local radius = tonumber(fields.radius); 175 | 176 | if x == nil or y == nil or z == nil or radius == nil or radius < 1 then 177 | return 178 | end 179 | 180 | local max_radius = jumpdrive.config.max_radius 181 | 182 | if radius > max_radius then 183 | minetest.chat_send_player(sender:get_player_name(), "Invalid jump: max-radius=" .. max_radius) 184 | return 185 | end 186 | 187 | -- update coords 188 | meta:set_int("x", jumpdrive.sanitize_coord(x)) 189 | meta:set_int("y", jumpdrive.sanitize_coord(y)) 190 | meta:set_int("z", jumpdrive.sanitize_coord(z)) 191 | meta:set_int("radius", radius) 192 | jumpdrive.update_formspec(meta, pos) 193 | 194 | if fields.jump then 195 | local success, msg = jumpdrive.execute_jump(pos, sender) 196 | if success then 197 | local time_millis = math.floor(msg / 1000) 198 | minetest.chat_send_player(sender:get_player_name(), "Jump executed in " .. time_millis .. " ms") 199 | else 200 | minetest.chat_send_player(sender:get_player_name(), msg) 201 | end 202 | end 203 | 204 | if fields.show then 205 | local success, msg = jumpdrive.simulate_jump(pos, sender, true) 206 | if not success then 207 | minetest.chat_send_player(sender:get_player_name(), msg) 208 | return 209 | end 210 | minetest.chat_send_player(sender:get_player_name(), "Simulation successful") 211 | end 212 | 213 | end, 214 | 215 | -- inventory protection 216 | allow_metadata_inventory_move = function(pos, _, _, _, _, count, player) 217 | if (not player) 218 | or (not player:is_player()) 219 | or minetest.is_protected(pos, player:get_player_name()) 220 | then return 0 end 221 | 222 | return count 223 | end, 224 | 225 | allow_metadata_inventory_take = function(pos, _, _, stack, player) 226 | if (not player) 227 | or (not player:is_player()) 228 | or minetest.is_protected(pos, player:get_player_name()) 229 | then return 0 end 230 | 231 | return stack:get_count() 232 | end, 233 | 234 | allow_metadata_inventory_put = function(pos, _, _, stack, player) 235 | if (not player) 236 | or (not player:is_player()) 237 | or minetest.is_protected(pos, player:get_player_name()) 238 | then return 0 end 239 | 240 | return stack:get_count() 241 | end, 242 | 243 | -- upgrade re-calculation 244 | on_metadata_inventory_put = function(pos, listname) 245 | if listname == "upgrade" then 246 | jumpdrive.upgrade.calculate(pos) 247 | end 248 | end, 249 | 250 | on_metadata_inventory_take = function(pos, listname) 251 | if listname == "upgrade" then 252 | jumpdrive.upgrade.calculate(pos) 253 | end 254 | end, 255 | 256 | on_metadata_inventory_move = function(pos, from_list, _, to_list) 257 | if from_list == "upgrade" or to_list == "upgrade" then 258 | jumpdrive.upgrade.calculate(pos) 259 | end 260 | end, 261 | 262 | on_punch = has_vizlib and function(pos, _, player) 263 | if not player or player:get_wielded_item():get_name() ~= "" then 264 | -- Only show jump area when using an empty hand 265 | return 266 | end 267 | local radius = minetest.get_meta(pos):get_int("radius") 268 | vizlib.draw_cube(pos, radius + 0.5, { color = "#00ff00", player = player }) 269 | end or nil, 270 | }) 271 | 272 | if minetest.get_modpath("technic") then 273 | technic.register_machine("HV", "jumpdrive:engine", technic.receiver) 274 | end 275 | 276 | -------------------------------------------------------------------------------- /fleet/fleet_controller.lua: -------------------------------------------------------------------------------- 1 | 2 | minetest.register_node("jumpdrive:fleet_controller", { 3 | description = "Jumpdrive Fleet controller", 4 | 5 | tiles = {"jumpdrive_fleet_controller.png"}, 6 | groups = {cracky=3,oddly_breakable_by_hand=3,handy=1,pickaxey=1}, 7 | _mcl_blast_resistance = 2, 8 | _mcl_hardness = 0.9, 9 | sounds = jumpdrive.sounds.node_sound_glass_defaults(), 10 | 11 | is_ground_content = false, 12 | light_source = 13, 13 | 14 | digiline = { 15 | receptor = { 16 | rules = jumpdrive.digiline_rules, 17 | action = function() end 18 | }, 19 | effector = { 20 | rules = jumpdrive.digiline_rules, 21 | action = jumpdrive.fleet.digiline_effector 22 | } 23 | }, 24 | 25 | after_place_node = function(pos, placer) 26 | local meta = minetest.get_meta(pos) 27 | -- owner of fleet_controller 28 | meta:set_string("owner", placer:get_player_name() or "") 29 | -- default digiline channel 30 | meta:set_string("channel", "fleetcontroller") 31 | 32 | jumpdrive.fleet.update_formspec(meta, pos) 33 | end, 34 | 35 | on_construct = function(pos) 36 | local meta = minetest.get_meta(pos) 37 | meta:set_int("x", pos.x) 38 | meta:set_int("y", pos.y) 39 | meta:set_int("z", pos.z) 40 | 41 | -- jumping active (1=active) 42 | meta:set_int("active", 0) 43 | -- if active, current index in jump list (1...n) 44 | meta:set_int("jump_index", 0) 45 | -- jump list 46 | meta:set_string("jump_list", "") 47 | 48 | local inv = meta:get_inventory() 49 | inv:set_size("main", 8) 50 | 51 | jumpdrive.fleet.update_formspec(meta, pos) 52 | end, 53 | 54 | can_dig = function(pos,player) 55 | local meta = minetest.get_meta(pos); 56 | local inv = meta:get_inventory() 57 | local name = player:get_player_name() 58 | 59 | return inv:is_empty("main") and not minetest.is_protected(pos, name) 60 | end, 61 | 62 | on_receive_fields = function(pos, _, fields, sender) 63 | 64 | local meta = minetest.get_meta(pos); 65 | 66 | if not sender then 67 | return 68 | end 69 | 70 | if minetest.is_protected(pos, sender:get_player_name()) then 71 | -- not allowed 72 | return 73 | end 74 | 75 | if fields.read_book then 76 | jumpdrive.read_from_book(pos) 77 | jumpdrive.fleet.update_formspec(meta, pos) 78 | return 79 | end 80 | 81 | if fields.reset then 82 | jumpdrive.reset_coordinates(pos) 83 | jumpdrive.fleet.update_formspec(meta, pos) 84 | return 85 | end 86 | 87 | if fields.write_book then 88 | jumpdrive.write_to_book(pos, sender) 89 | return 90 | end 91 | 92 | if fields.set_digiline_channel and fields.digiline_channel then 93 | meta:set_string("channel", fields.digiline_channel) 94 | jumpdrive.fleet.update_formspec(meta, pos) 95 | return 96 | end 97 | 98 | local x = tonumber(fields.x); 99 | local y = tonumber(fields.y); 100 | local z = tonumber(fields.z); 101 | 102 | if x == nil or y == nil or z == nil then 103 | return 104 | end 105 | 106 | -- update coords 107 | meta:set_int("x", jumpdrive.sanitize_coord(x)) 108 | meta:set_int("y", jumpdrive.sanitize_coord(y)) 109 | meta:set_int("z", jumpdrive.sanitize_coord(z)) 110 | jumpdrive.fleet.update_formspec(meta, pos) 111 | 112 | local t0 = minetest.get_us_time() 113 | local engines_pos_list = jumpdrive.fleet.find_engines(pos) 114 | local t1 = minetest.get_us_time() 115 | minetest.log("action", "[jumpdrive-fleet] backbone traversing took " .. 116 | (t1 - t0) .. " us @ " .. minetest.pos_to_string(pos)) 117 | 118 | local targetPos = {x=meta:get_int("x"),y=meta:get_int("y"),z=meta:get_int("z")} 119 | 120 | -- sort by distance, farthes first 121 | jumpdrive.fleet.sort_engines(pos, engines_pos_list) 122 | 123 | -- apply new coordinates 124 | jumpdrive.fleet.apply_coordinates(pos, targetPos, engines_pos_list) 125 | 126 | if fields.jump then 127 | --TODO check overlapping engines/radius 128 | meta:set_int("active", 1) 129 | meta:set_int("jump_index", 1) 130 | meta:set_string("jump_list", minetest.serialize(engines_pos_list)) 131 | jumpdrive.fleet.update_formspec(meta, pos) 132 | 133 | local timer = minetest.get_node_timer(pos) 134 | timer:start(2.0) 135 | end 136 | 137 | if fields.stop then 138 | meta:set_int("active", 0) 139 | local timer = minetest.get_node_timer(pos) 140 | timer:stop() 141 | jumpdrive.fleet.update_formspec(meta, pos) 142 | end 143 | 144 | if fields.show then 145 | local playername = sender:get_player_name() 146 | minetest.chat_send_player(playername, "Found " .. #engines_pos_list .. " jumpdrives") 147 | 148 | if #engines_pos_list == 0 then 149 | return 150 | end 151 | 152 | local index = 1 153 | local async_check 154 | async_check = function() 155 | local engine_pos = engines_pos_list[index] 156 | local success, msg = jumpdrive.simulate_jump(engine_pos, sender, true) 157 | if not success then 158 | minetest.chat_send_player(playername, msg .. " @ " .. minetest.pos_to_string(engine_pos)) 159 | return 160 | end 161 | 162 | minetest.chat_send_player(sender:get_player_name(), "Check passed for engine " .. index .. "/" .. #engines_pos_list) 163 | 164 | if index < #engines_pos_list then 165 | -- more drives to check 166 | index = index + 1 167 | minetest.after(1, async_check) 168 | 169 | elseif index >= #engines_pos_list then 170 | -- done 171 | minetest.chat_send_player(sender:get_player_name(), "Simulation successful") 172 | end 173 | end 174 | 175 | minetest.after(1, async_check) 176 | end 177 | 178 | end, 179 | 180 | on_timer = function(pos) 181 | local meta = minetest.get_meta(pos) 182 | local jump_index = meta:get_int("jump_index") 183 | local jump_list = minetest.deserialize( meta:get_string("jump_list") ) 184 | 185 | if jump_list and jump_index and #jump_list >= jump_index then 186 | 187 | local is_last = #jump_list == jump_index 188 | local node_pos = jump_list[jump_index] 189 | local success, msg = jumpdrive.execute_jump(node_pos) 190 | 191 | local playername = meta:get_string("owner") 192 | if not playername then 193 | local node_meta = minetest.get_meta(node_pos) 194 | playername = node_meta:get_string("owner") 195 | end 196 | 197 | if success then 198 | -- at this point if it is the last engine the metadata does not exist anymore in the current location 199 | 200 | if not is_last then 201 | meta:set_int("jump_index", jump_index+1) 202 | jumpdrive.fleet.update_formspec(meta, pos) 203 | 204 | -- re-schedule 205 | local timer = minetest.get_node_timer(pos) 206 | timer:start(2.0) 207 | end 208 | if playername then 209 | local time_millis = math.floor(msg / 1000) 210 | minetest.chat_send_player(playername, "Jump executed in " .. time_millis .. " ms") 211 | end 212 | else 213 | meta:set_int("active", 0) 214 | jumpdrive.fleet.update_formspec(meta, pos) 215 | meta:set_string("infotext", "Engine ".. minetest.pos_to_string(node_pos) .. " failed with: " .. msg) 216 | if playername then 217 | minetest.chat_send_player(playername, msg) 218 | end 219 | end 220 | else 221 | meta:set_int("active", 0) 222 | jumpdrive.fleet.update_formspec(meta, pos) 223 | end 224 | end 225 | }) 226 | -------------------------------------------------------------------------------- /fleet/fleet_digiline.lua: -------------------------------------------------------------------------------- 1 | 2 | local is_int = function(value) 3 | return type(value) == 'number' and math.floor(value) == value 4 | end 5 | 6 | jumpdrive.fleet.is_active = function(pos) 7 | local meta = minetest.get_meta(pos) 8 | return meta:get_int("active") > 0 9 | end 10 | 11 | jumpdrive.fleet.get_fleet_data = function(pos, target_pos, engines_pos_list) 12 | local t0 = minetest.get_us_time() 13 | 14 | -- get some more or less useful data from fleet 15 | local engines = {} 16 | local max_power_req = 0 17 | local total_power_req = 0 18 | local distance = nil 19 | local active = jumpdrive.fleet.is_active(pos) 20 | 21 | local delta_vector = nil 22 | if target_pos then 23 | delta_vector = vector.subtract(target_pos, pos) 24 | distance = vector.distance(pos, target_pos) 25 | end 26 | 27 | if not active then 28 | for _,engine_pos in ipairs(engines_pos_list) do 29 | local engine_meta = minetest.get_meta(engine_pos) 30 | local powerstorage = engine_meta:get_int("powerstorage") 31 | local radius = jumpdrive.get_radius(engine_pos) 32 | local engine_target_pos = nil 33 | local engine_distance = nil 34 | local engine_power_req = 0 35 | if target_pos then 36 | engine_target_pos = vector.add(engine_pos, delta_vector) 37 | engine_distance = vector.distance(engine_pos, engine_target_pos) 38 | engine_power_req = jumpdrive.calculate_power(radius, engine_distance, engine_pos, engine_target_pos) 39 | if engine_power_req > max_power_req then 40 | max_power_req = engine_power_req 41 | end 42 | total_power_req = total_power_req + engine_power_req 43 | end 44 | table.insert(engines, { 45 | power_req = engine_power_req, 46 | powerstorage = powerstorage, 47 | radius = radius, 48 | position = engine_pos, 49 | target = engine_target_pos, 50 | distance = engine_distance, 51 | }) 52 | end 53 | end 54 | 55 | local t1 = minetest.get_us_time() 56 | minetest.log("action", "[jumpdrive-fleet] fleet data collection took " .. 57 | (t1 - t0) .. " us @ " .. minetest.pos_to_string(pos)) 58 | 59 | return { 60 | active = active, 61 | engines = engines, 62 | max_power_req = max_power_req, 63 | total_power_req = total_power_req, 64 | position = pos, 65 | target = target_pos, 66 | distance = distance, 67 | } 68 | end 69 | 70 | jumpdrive.fleet.get_engines_sorted = function(pos) 71 | local t0 = minetest.get_us_time() 72 | local engines_pos_list = jumpdrive.fleet.find_engines(pos) 73 | local t1 = minetest.get_us_time() 74 | minetest.log("action", "[jumpdrive-fleet] backbone traversing took " .. 75 | (t1 - t0) .. " us @ " .. minetest.pos_to_string(pos)) 76 | 77 | -- sort by distance, farthes first 78 | jumpdrive.fleet.sort_engines(pos, engines_pos_list) 79 | 80 | return engines_pos_list 81 | end 82 | 83 | jumpdrive.fleet.digiline_async_simulate = function(pos, channel, owner, engines) 84 | local index = 1 85 | local meta = minetest.get_meta(pos) 86 | local msglist = {} 87 | local async_check 88 | async_check = function() 89 | if meta:get_int("active") < 1 then 90 | -- operation aborted by user while there's still work to do 91 | digilines.receptor_send(pos, jumpdrive.digiline_rules, channel, { 92 | success = false, 93 | index = index, 94 | count = #engines, 95 | msg = "simulation aborted", 96 | }) 97 | return 98 | end 99 | 100 | local engine_pos = engines[index] 101 | local success, msg = jumpdrive.simulate_jump(engine_pos, owner, false) 102 | 103 | if not success then 104 | digilines.receptor_send(pos, jumpdrive.digiline_rules, channel, { 105 | success=false, 106 | pos=engine_pos, 107 | msg=msg, 108 | }) 109 | meta:set_int("active", 0) 110 | jumpdrive.fleet.update_formspec(meta, pos) 111 | return 112 | end 113 | table.insert(msglist, msg) 114 | 115 | if index < #engines then 116 | -- more drives to check 117 | index = index + 1 118 | minetest.after(1, async_check) 119 | 120 | elseif index >= #engines then 121 | -- done 122 | digilines.receptor_send(pos, jumpdrive.digiline_rules, channel, { 123 | success=true, 124 | count=index, 125 | msgs=msglist, 126 | }) 127 | meta:set_int("active", 0) 128 | jumpdrive.fleet.update_formspec(meta, pos) 129 | end 130 | end 131 | meta:set_string("jump_list", minetest.serialize(engines)) 132 | meta:set_int("active", 1) 133 | jumpdrive.fleet.update_formspec(meta, pos) 134 | minetest.after(1, async_check) 135 | end 136 | 137 | jumpdrive.fleet.digiline_async_jump = function(pos, target_pos, channel, _, engines) 138 | local t0 = minetest.get_us_time() 139 | local meta = minetest.get_meta(pos) 140 | local all_success = false 141 | local msglist = {} 142 | local index = 1 143 | local async_jump 144 | async_jump = function() 145 | if engines and index and #engines >= index then 146 | if meta:get_int("active") < 1 then 147 | -- operation aborted by user while there's still work to do 148 | digilines.receptor_send(pos, jumpdrive.digiline_rules, channel, { 149 | success = false, 150 | index = index, 151 | count = #engines, 152 | msg = "jump aborted", 153 | }) 154 | return 155 | end 156 | 157 | local node_pos = engines[index] 158 | local success, msg = jumpdrive.execute_jump(node_pos) 159 | table.insert(msglist, msg) 160 | 161 | if success then 162 | -- at this point if it is the last engine the metadata does not exist anymore in the current location 163 | 164 | if #engines == index then 165 | -- last engine jumped successfully 166 | all_success = true 167 | else 168 | meta:set_int("jump_index", index) 169 | end 170 | -- execute for each engine and once after last engine jumped 171 | index = index + 1 172 | minetest.after(1, async_jump) 173 | else 174 | digilines.receptor_send(pos, jumpdrive.digiline_rules, channel, { 175 | success = false, 176 | count = index, 177 | msg = msg, 178 | msgs = msglist, 179 | }) 180 | meta:set_int("active", 0) 181 | jumpdrive.fleet.update_formspec(meta, pos) 182 | end 183 | else 184 | local targetmeta = minetest.get_meta(target_pos) 185 | local t1 = minetest.get_us_time() 186 | digilines.receptor_send(target_pos, jumpdrive.digiline_rules, channel, { 187 | success = all_success, -- in case someone calls with zero engines should it be success or not? 188 | count = index, 189 | msgs = msglist, 190 | time = t1 - t0 191 | }) 192 | targetmeta:set_int("active", 0) 193 | jumpdrive.fleet.update_formspec(targetmeta, target_pos) 194 | end 195 | end 196 | meta:set_string("jump_list", minetest.serialize(engines)) 197 | meta:set_int("active", 1) 198 | jumpdrive.fleet.update_formspec(meta, pos) 199 | minetest.after(1, async_jump) 200 | end 201 | 202 | jumpdrive.fleet.digiline_effector = function(pos, _, channel, msg) 203 | 204 | if type(msg) ~= "table" then 205 | return 206 | end 207 | 208 | local meta = minetest.get_meta(pos) 209 | 210 | local set_channel = meta:get_string("channel") 211 | if channel ~= set_channel then 212 | return 213 | end 214 | 215 | local playername = meta:get_string("owner") 216 | if not playername then 217 | return 218 | end 219 | 220 | local targetPos = jumpdrive.get_meta_pos(pos) 221 | 222 | if msg.command == "get" then 223 | 224 | local engines_pos_list = jumpdrive.fleet.find_engines(pos) 225 | local fleetdata = jumpdrive.fleet.get_fleet_data(pos, targetPos, engines_pos_list) 226 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, fleetdata) 227 | 228 | elseif msg.command == "reset" then 229 | 230 | if jumpdrive.fleet.is_active(pos) then 231 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 232 | success = false, 233 | msg = "Operation not completed", 234 | }) 235 | return 236 | end 237 | 238 | meta:set_int("x", pos.x) 239 | meta:set_int("y", pos.y) 240 | meta:set_int("z", pos.z) 241 | jumpdrive.fleet.update_formspec(meta, pos) 242 | 243 | elseif msg.command == "set" then 244 | 245 | if jumpdrive.fleet.is_active(pos) then 246 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 247 | success = false, 248 | msg = "Operation not completed", 249 | }) 250 | return 251 | end 252 | 253 | -- API requires integers for coord values, noop for everything else 254 | if is_int(msg.x) then meta:set_int("x", jumpdrive.sanitize_coord(msg.x)) end 255 | if is_int(msg.y) then meta:set_int("y", jumpdrive.sanitize_coord(msg.y)) end 256 | if is_int(msg.z) then meta:set_int("z", jumpdrive.sanitize_coord(msg.z)) end 257 | if msg.formupdate then 258 | jumpdrive.fleet.update_formspec(meta, pos) 259 | end 260 | 261 | elseif msg.command == "simulate" or msg.command == "show" then 262 | 263 | if jumpdrive.fleet.is_active(pos) then 264 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 265 | success = false, 266 | msg = "Operation not completed", 267 | }) 268 | return 269 | end 270 | 271 | local engines_pos_list = jumpdrive.fleet.get_engines_sorted(pos) 272 | 273 | if #engines_pos_list == 0 then 274 | return 275 | end 276 | 277 | -- apply new coordinates 278 | jumpdrive.fleet.apply_coordinates(pos, targetPos, engines_pos_list) 279 | 280 | local owner = minetest.get_player_by_name(playername) 281 | jumpdrive.fleet.digiline_async_simulate(pos, channel, owner, engines_pos_list) 282 | 283 | elseif msg.command == "jump" then 284 | 285 | if jumpdrive.fleet.is_active(pos) then 286 | digilines.receptor_send(pos, jumpdrive.digiline_rules, set_channel, { 287 | success = false, 288 | msg = "Operation not completed", 289 | }) 290 | return 291 | end 292 | 293 | local engines_pos_list = jumpdrive.fleet.get_engines_sorted(pos) 294 | 295 | if #engines_pos_list == 0 then 296 | return 297 | end 298 | 299 | -- apply new coordinates 300 | jumpdrive.fleet.apply_coordinates(pos, targetPos, engines_pos_list) 301 | 302 | local owner = minetest.get_player_by_name(playername) 303 | jumpdrive.fleet.digiline_async_jump(pos, targetPos, channel, owner, engines_pos_list) 304 | 305 | end 306 | end 307 | -------------------------------------------------------------------------------- /fleet/fleet_formspec.lua: -------------------------------------------------------------------------------- 1 | 2 | local inv_width = 8 3 | local inv_height = 10 4 | 5 | local player_inv_fs = "list[current_player;main;0,5;8,4;]" 6 | local listring_fs = "listring[]" 7 | local mcl_fs = "" 8 | 9 | if minetest.get_modpath("mcl_formspec") then 10 | inv_width = 9 11 | inv_height = 10.5 12 | mcl_fs = mcl_formspec.get_itemslot_bg(0,3.75,8,1) 13 | player_inv_fs = "".. 14 | "list[current_player;main;0,4.9;9,3;9]".. 15 | mcl_formspec.get_itemslot_bg(0,4.9,9,3).. 16 | "list[current_player;main;0,8.05;9,1;]".. 17 | mcl_formspec.get_itemslot_bg(0,8.05,9,1) 18 | listring_fs = "listring[current_player;main]" 19 | end 20 | 21 | jumpdrive.fleet.update_formspec = function(meta) 22 | 23 | local button_line = 24 | "button_exit[0,1.5;2,1;jump;Jump]" .. 25 | "button_exit[2,1.5;2,1;show;Show]" .. 26 | "button_exit[4,1.5;2,1;save;Save]" .. 27 | "button[6,1.5;2,1;reset;Reset]" 28 | 29 | if meta:get_int("active") == 1 then 30 | local jump_index = meta:get_int("jump_index") 31 | local jump_list = minetest.deserialize( meta:get_string("jump_list") ) 32 | 33 | meta:set_string("infotext", "Controller active: " .. jump_index .. "/" .. #jump_list) 34 | 35 | button_line = "button_exit[0,1.5;8,1;stop;Stop]" 36 | else 37 | meta:set_string("infotext", "Ready") 38 | end 39 | 40 | meta:set_string("formspec", "size["..inv_width..","..inv_height..";]" .. 41 | "field[0.3,0.5;2,1;x;X;" .. meta:get_int("x") .. "]" .. 42 | "field[3.3,0.5;2,1;y;Y;" .. meta:get_int("y") .. "]" .. 43 | "field[6.3,0.5;2,1;z;Z;" .. meta:get_int("z") .. "]" .. 44 | 45 | button_line .. 46 | 47 | "button[0,2.5;4,1;write_book;Write to book]" .. 48 | "button[4,2.5;4,1;read_book;Read from bookmark]" .. 49 | 50 | "list[context;main;0,3.75;8,1;]" .. 51 | 52 | player_inv_fs.. 53 | 54 | "field[4.3,9.52;3.2,1;digiline_channel;Digiline channel;" .. (meta:get_string("channel") or "") .. "]" .. 55 | "button_exit[7,9.2;1,1;set_digiline_channel;Set]" .. 56 | 57 | -- listring stuff 58 | listring_fs.. 59 | 60 | -- mcl 61 | mcl_fs) 62 | end 63 | 64 | -------------------------------------------------------------------------------- /fleet/fleet_functions.lua: -------------------------------------------------------------------------------- 1 | 2 | jumpdrive.fleet = {} 3 | 4 | -- applies the new coordinates derived from the relative position of the controller 5 | jumpdrive.fleet.apply_coordinates = function(controllerPos, targetPos, engine_pos_list) 6 | -- delta between source and target 7 | local delta_vector = vector.subtract(targetPos, controllerPos) 8 | minetest.log("action", "[jumpdrive-fleet] delta-vector: " .. minetest.pos_to_string(delta_vector)) 9 | 10 | for _,node_pos in pairs(engine_pos_list) do 11 | local new_pos = vector.add(node_pos, delta_vector) 12 | minetest.log("action", "[jumpdrive-fleet] calculated vector: " .. minetest.pos_to_string(new_pos)) 13 | 14 | -- set destination position 15 | jumpdrive.set_meta_pos(node_pos, new_pos) 16 | -- update formspec to reflect new positions 17 | jumpdrive.update_formspec(minetest.get_meta(node_pos), node_pos) 18 | end 19 | end 20 | 21 | -- sort list by distance, farthest first 22 | jumpdrive.fleet.sort_engines = function(pos, engine_pos_list) 23 | table.sort(engine_pos_list, function(a,b) 24 | local a_distance = vector.distance(a, pos) 25 | local b_distance = vector.distance(b, pos) 26 | return a_distance > b_distance 27 | end) 28 | end 29 | 30 | -- traverses the backbone and returns a list of engines 31 | jumpdrive.fleet.find_engines = function(pos, visited_hashes, engine_pos_list) 32 | 33 | -- minetest.hash_node_position(pos) 34 | visited_hashes = visited_hashes or {} 35 | engine_pos_list = engine_pos_list or {} 36 | 37 | local pos1 = vector.subtract(pos, 1) 38 | local pos2 = vector.add(pos,1) 39 | 40 | -- load far-away areas 41 | local manip = minetest.get_voxel_manip() 42 | manip:read_from_map(pos1, pos2) 43 | 44 | local engine_nodes = minetest.find_nodes_in_area(pos1, pos2, {"jumpdrive:engine"}) 45 | 46 | for _,node_pos in pairs(engine_nodes) do 47 | local hash = minetest.hash_node_position(node_pos) 48 | 49 | if not visited_hashes[hash] then 50 | -- pos not yet visited 51 | visited_hashes[hash] = true 52 | minetest.log("action", "[jumpdrive-fleet] adding engine @ " .. minetest.pos_to_string(node_pos)) 53 | table.insert(engine_pos_list, node_pos) 54 | 55 | jumpdrive.fleet.find_engines(node_pos, visited_hashes, engine_pos_list) 56 | end 57 | end 58 | 59 | local backbone_nodes = minetest.find_nodes_in_area(pos1, pos2, {"jumpdrive:backbone"}) 60 | 61 | for _,node_pos in pairs(backbone_nodes) do 62 | local hash = minetest.hash_node_position(node_pos) 63 | 64 | if not visited_hashes[hash] then 65 | -- pos not yet visited 66 | visited_hashes[hash] = true 67 | 68 | jumpdrive.fleet.find_engines(node_pos, visited_hashes, engine_pos_list) 69 | end 70 | end 71 | 72 | 73 | 74 | return engine_pos_list 75 | end 76 | -------------------------------------------------------------------------------- /formspec.lua: -------------------------------------------------------------------------------- 1 | local has_technic = minetest.get_modpath("technic") 2 | 3 | local inv_offset = 0 4 | if has_technic then 5 | inv_offset = 1.25 6 | end 7 | 8 | local inv_width = 8 9 | 10 | local mcl_fs = "" 11 | local player_inv_fs = "list[current_player;main;0,".. 4.5+inv_offset .. ";8,4;]" 12 | 13 | if minetest.get_modpath("mcl_formspec") then 14 | inv_width = 9 15 | mcl_fs = mcl_formspec.get_itemslot_bg(0,3.25,8,1) 16 | player_inv_fs = "".. 17 | "list[current_player;main;0,5.6;9,3;9]".. 18 | mcl_formspec.get_itemslot_bg(0,5.6,9,3).. 19 | "list[current_player;main;0,8.74;9,1;]".. 20 | mcl_formspec.get_itemslot_bg(0,8.74,9,1) 21 | if has_technic then 22 | mcl_fs = mcl_fs.. 23 | mcl_formspec.get_itemslot_bg(4,4.5,4,1) 24 | end 25 | end 26 | 27 | jumpdrive.update_formspec = function(meta) 28 | local formspec = 29 | "size["..inv_width.."," .. 9.3+inv_offset .. ";]" .. 30 | 31 | "field[0.3,0.5;2,1;x;X;" .. meta:get_int("x") .. "]" .. 32 | "field[2.3,0.5;2,1;y;Y;" .. meta:get_int("y") .. "]" .. 33 | "field[4.3,0.5;2,1;z;Z;" .. meta:get_int("z") .. "]" .. 34 | "field[6.3,0.5;2,1;radius;Radius;" .. meta:get_int("radius") .. "]" .. 35 | 36 | "button_exit[0,1;2,1;jump;Jump]" .. 37 | "button_exit[2,1;2,1;show;Show]" .. 38 | "button_exit[4,1;2,1;save;Save]" .. 39 | "button[6,1;2,1;reset;Reset]" .. 40 | 41 | "button[0,2;4,1;write_book;Write to book]" .. 42 | "button[4,2;4,1;read_book;Read from bookmark]" .. 43 | 44 | -- main inventory for fuel and books 45 | "list[context;main;0,3.25;8,1;]" .. 46 | 47 | -- player inventory 48 | player_inv_fs.. 49 | 50 | -- digiline channel 51 | "field[4.3," .. 9.02+inv_offset ..";3.2,1;digiline_channel;Digiline channel;" .. 52 | (meta:get_string("channel") or "") .. "]" .. 53 | "button_exit[7," .. 8.7+inv_offset .. ";1,1;set_digiline_channel;Set]" .. 54 | 55 | -- listring stuff 56 | "listring[context;main]" .. 57 | "listring[current_player;main]".. 58 | 59 | -- mcl 60 | mcl_fs 61 | 62 | if has_technic then 63 | formspec = formspec .. 64 | -- technic upgrades 65 | "label[3,4.7;Upgrades]" .. 66 | "list[context;upgrade;4,4.5;4,1;]" 67 | end 68 | 69 | meta:set_string("formspec", formspec) 70 | end 71 | 72 | -------------------------------------------------------------------------------- /fuel.lua: -------------------------------------------------------------------------------- 1 | 2 | local fuel_list = {} 3 | 4 | jumpdrive.fuel = {} 5 | 6 | jumpdrive.fuel.register = function(item_name, value) 7 | fuel_list[item_name] = value 8 | end 9 | 10 | jumpdrive.fuel.get_value = function(item_name) 11 | if not item_name then 12 | return 0 13 | end 14 | return fuel_list[item_name] or 0 15 | end 16 | 17 | if minetest.get_modpath("default") then 18 | jumpdrive.fuel.register("default:mese_crystal_fragment", 100) 19 | jumpdrive.fuel.register("default:mese_crystal", 900) 20 | jumpdrive.fuel.register("default:mese", 8100) 21 | end 22 | 23 | if minetest.get_modpath("mcl_core") and minetest.get_modpath("mesecons") and minetest.get_modpath("mesecons_torch") then 24 | jumpdrive.fuel.register("mesecons:wire_00000000_off", 900) 25 | jumpdrive.fuel.register("mesecons_torch:redstoneblock", 8100) 26 | end 27 | -------------------------------------------------------------------------------- /hooks.lua: -------------------------------------------------------------------------------- 1 | 2 | -- callback for after-jump actions 3 | 4 | local after_jump_callbacks = {} 5 | 6 | function jumpdrive.register_after_jump(callback) 7 | table.insert(after_jump_callbacks, callback) 8 | end 9 | 10 | function jumpdrive.fire_after_jump(from_area, to_area) 11 | for _, callback in ipairs(after_jump_callbacks) do 12 | callback(from_area, to_area) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /infotext.lua: -------------------------------------------------------------------------------- 1 | local has_technic = minetest.get_modpath("technic") 2 | 3 | jumpdrive.update_infotext = function(meta) 4 | local store = meta:get_int("powerstorage") 5 | local max_store = meta:get_int("max_powerstorage") 6 | 7 | if has_technic then 8 | local eu_input = meta:get_int("HV_EU_input") 9 | local demand = meta:get_int("HV_EU_demand") 10 | 11 | meta:set_string("infotext", "Power: " .. eu_input .. "/" .. demand .. " Store: " .. store .. "/" .. max_store) 12 | else 13 | meta:set_string("infotext", "Store: " .. store .. "/" .. max_store) 14 | end 15 | end -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | 2 | jumpdrive = { 3 | config = { 4 | -- allowed radius 5 | max_radius = tonumber(minetest.settings:get("jumpdrive.max_radius")) or 15, 6 | max_area_radius = tonumber(minetest.settings:get("jumpdrive.max_area_radius")) or 25, 7 | 8 | -- max volume in nodes ( ((radius*2) + 1) ^ 3 ) 9 | max_area_volume = tonumber(minetest.settings:get("jumpdrive.max_area_volume")) or 29791, 10 | 11 | -- base storage value 12 | powerstorage = tonumber(minetest.settings:get("jumpdrive.powerstorage")) or 1000000, 13 | 14 | -- base technic power requirement 15 | powerrequirement = tonumber(minetest.settings:get("jumpdrive.power_requirement")) or 2500, 16 | 17 | -- allow emerging area on "uncharted" error 18 | emerge_uncharted = core.settings:get_bool("jumpdrive.allow_emerge", false), 19 | }, 20 | 21 | -- blacklisted nodes 22 | blacklist = {} 23 | } 24 | 25 | jumpdrive.sounds = {} 26 | 27 | if minetest.get_modpath("default") then 28 | jumpdrive.sounds = default 29 | end 30 | 31 | if minetest.get_modpath("mcl_sounds") then 32 | jumpdrive.sounds = mcl_sounds 33 | end 34 | 35 | local MP = minetest.get_modpath("jumpdrive") 36 | 37 | if minetest.get_modpath("technic") then 38 | dofile(MP.."/technic_run.lua") 39 | end 40 | 41 | -- common functions 42 | dofile(MP.."/fuel.lua") 43 | dofile(MP.."/upgrade.lua") 44 | dofile(MP.."/bookmark.lua") 45 | dofile(MP.."/infotext.lua") 46 | dofile(MP.."/migrate.lua") 47 | dofile(MP.."/hooks.lua") 48 | dofile(MP.."/compat/compat.lua") 49 | dofile(MP.."/is_area_empty.lua") 50 | dofile(MP.."/is_area_protected.lua") 51 | 52 | -- move logic 53 | dofile(MP.."/move/move_objects.lua") 54 | dofile(MP.."/move/move_mapdata.lua") 55 | dofile(MP.."/move/move_metadata.lua") 56 | dofile(MP.."/move/move_nodetimers.lua") 57 | dofile(MP.."/move/move_players.lua") 58 | dofile(MP.."/move/move.lua") 59 | 60 | dofile(MP.."/mapgen.lua") 61 | dofile(MP.."/common.lua") 62 | dofile(MP.."/digiline.lua") 63 | dofile(MP.."/backbone.lua") 64 | dofile(MP.."/warp_device.lua") 65 | dofile(MP.."/crafts.lua") 66 | 67 | -- engine 68 | dofile(MP.."/engine.lua") 69 | dofile(MP.."/formspec.lua") 70 | dofile(MP.."/jump.lua") 71 | 72 | -- fleet 73 | dofile(MP.."/fleet/fleet_functions.lua") 74 | dofile(MP.."/fleet/fleet_digiline.lua") 75 | dofile(MP.."/fleet/fleet_controller.lua") 76 | dofile(MP.."/fleet/fleet_formspec.lua") 77 | 78 | -- blacklist nodes 79 | dofile(MP.."/blacklist.lua") 80 | 81 | 82 | if minetest.get_modpath("monitoring") then 83 | -- enable metrics 84 | dofile(MP.."/metrics.lua") 85 | end 86 | 87 | if minetest.get_modpath("mtt") and mtt.enabled then 88 | dofile(MP.."/mtt.lua") 89 | end 90 | 91 | print("[OK] Jumpdrive") 92 | -------------------------------------------------------------------------------- /is_area_empty.lua: -------------------------------------------------------------------------------- 1 | local c_ignore = minetest.get_content_id("ignore") 2 | 3 | local buildable_to_nodes = {} 4 | 5 | minetest.after(4, function() 6 | local count = 0 7 | for name, node in pairs(minetest.registered_nodes) do 8 | if node.buildable_to then 9 | count = count + 1 10 | local id = minetest.get_content_id(name) 11 | buildable_to_nodes[id] = true 12 | end 13 | end 14 | minetest.log("action", "[jumpdrive] collected " .. count .. " nodes that are buildable_to") 15 | end) 16 | 17 | 18 | 19 | jumpdrive.is_area_empty = function(pos1, pos2) 20 | local manip = minetest.get_voxel_manip() 21 | local e1, e2 = manip:read_from_map(pos1, pos2) 22 | local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2}) 23 | local data = manip:get_data() 24 | 25 | for z=pos1.z, pos2.z do 26 | for y=pos1.y, pos2.y do 27 | for x=pos1.x, pos2.x do 28 | 29 | local index = area:index(x, y, z) 30 | local id = data[index] 31 | 32 | if id == c_ignore then 33 | return false, "uncharted" 34 | end 35 | 36 | if not buildable_to_nodes[id] then 37 | -- not buildable_to 38 | return false, "occupied" 39 | end 40 | end 41 | end 42 | end 43 | 44 | -- only buildable_to nodes found 45 | return true, "" 46 | end 47 | 48 | -------------------------------------------------------------------------------- /is_area_protected.lua: -------------------------------------------------------------------------------- 1 | local has_areas_mod = minetest.get_modpath("areas") 2 | local has_protector_mod = minetest.get_modpath("protector") 3 | 4 | local protector_radius = (tonumber(minetest.settings:get("protector_radius")) or 5) 5 | 6 | jumpdrive.is_area_protected = function(pos1, pos2, playername) 7 | 8 | local radius_vector = {x=protector_radius, y=protector_radius, z=protector_radius} 9 | local check_pos1 = vector.subtract(pos1, radius_vector) 10 | local check_pos2 = vector.add(pos2, radius_vector) 11 | 12 | -- preload area with voxel manip 13 | minetest.get_voxel_manip(check_pos1, check_pos2) 14 | 15 | if minetest.is_area_protected then 16 | -- use area protection check 17 | if minetest.is_area_protected(pos1, pos2, playername, 8) then 18 | return true 19 | end 20 | 21 | elseif has_protector_mod then 22 | -- use improvised find_nodes check 23 | local protectors = minetest.find_nodes_in_area( 24 | check_pos1, check_pos2, { 25 | "protector:protect", 26 | "protector:protect2", 27 | "priv_protector:protector", 28 | "xp_redo:protector" 29 | } 30 | ) 31 | 32 | if protectors then 33 | for _,pos in pairs(protectors) do 34 | if minetest.is_protected(pos, playername) then 35 | return true 36 | end 37 | end 38 | end 39 | end 40 | 41 | if has_areas_mod then 42 | if not areas:canInteractInArea(pos1, pos2, playername, true) then 43 | -- player can't interact 44 | return true 45 | end 46 | end 47 | 48 | -- no protection 49 | return false 50 | end 51 | -------------------------------------------------------------------------------- /jump.lua: -------------------------------------------------------------------------------- 1 | local has_vizlib = minetest.get_modpath("vizlib") 2 | 3 | jumpdrive.simulate_jump = function(pos, player, show_marker) 4 | 5 | local targetPos = jumpdrive.get_meta_pos(pos) 6 | 7 | local mapgen_distance = jumpdrive.check_mapgen(pos) 8 | if mapgen_distance then 9 | return false, "Error: mapgen was active "..math.floor(mapgen_distance) 10 | .. " / 200 nodes away, please try again later for your own safety!" 11 | end 12 | 13 | local meta = minetest.get_meta(pos) 14 | 15 | if show_marker and has_vizlib and os.time() < meta:get_int("simulation_expiry") then 16 | return false, "Error: simulation is still active! please wait before simulating again" 17 | end 18 | 19 | local radius = jumpdrive.get_radius(pos) 20 | local distance = vector.distance(pos, targetPos) 21 | 22 | local playername = meta:get_string("owner") 23 | 24 | if player ~= nil then 25 | playername = player:get_player_name() 26 | end 27 | 28 | local radius_vector = {x=radius, y=radius, z=radius} 29 | local source_pos1 = vector.subtract(pos, radius_vector) 30 | local source_pos2 = vector.add(pos, radius_vector) 31 | local target_pos1 = vector.subtract(targetPos, radius_vector) 32 | local target_pos2 = vector.add(targetPos, radius_vector) 33 | 34 | local x_overlap = (target_pos1.x <= source_pos2.x and target_pos1.x >= source_pos1.x) or 35 | (target_pos2.x <= source_pos2.x and target_pos2.x >= source_pos1.x) 36 | local y_overlap = (target_pos1.y <= source_pos2.y and target_pos1.y >= source_pos1.y) or 37 | (target_pos2.y <= source_pos2.y and target_pos2.y >= source_pos1.y) 38 | local z_overlap = (target_pos1.z <= source_pos2.z and target_pos1.z >= source_pos1.z) or 39 | (target_pos2.z <= source_pos2.z and target_pos2.z >= source_pos1.z) 40 | 41 | if x_overlap and y_overlap and z_overlap then 42 | return false, "Error: jump into itself! extend your jump target" 43 | end 44 | 45 | -- load chunk 46 | minetest.get_voxel_manip():read_from_map(target_pos1, target_pos2) 47 | 48 | if show_marker and has_vizlib then 49 | vizlib.draw_cube(targetPos, radius + 0.5, { color = "#ff0000" }) 50 | vizlib.draw_cube(pos, radius + 0.5, { color = "#00ff00" }) 51 | local shape = vizlib.draw_point(targetPos, { color = "#0000ff" }) 52 | meta:set_int("simulation_expiry", shape.expiry) 53 | end 54 | 55 | local msg = "" 56 | local success = true 57 | 58 | local blacklisted_pos_list = minetest.find_nodes_in_area(source_pos1, source_pos2, jumpdrive.blacklist) 59 | local _, nodepos = next(blacklisted_pos_list) 60 | if nodepos then 61 | return false, "Can't jump node @ " .. minetest.pos_to_string(nodepos) 62 | end 63 | 64 | if minetest.find_node_near(targetPos, radius, "vacuum:vacuum", true) then 65 | msg = msg .. "\nWarning: Jump-target is in vacuum!" 66 | end 67 | 68 | -- -- found to be useless/indescriptive and is superseded by "Jump-target is uncharted" 69 | -- if minetest.find_node_near(targetPos, radius, "ignore", true) then 70 | -- return false, "Warning: Jump-target is in uncharted area" 71 | -- end 72 | 73 | if jumpdrive.is_area_protected(source_pos1, source_pos2, playername) then 74 | return false, "Jump-source is protected!" 75 | end 76 | 77 | if jumpdrive.is_area_protected(target_pos1, target_pos2, playername) then 78 | return false, "Jump-target is protected!" 79 | end 80 | 81 | local is_empty, empty_msg = jumpdrive.is_area_empty(target_pos1, target_pos2) 82 | 83 | if not is_empty then 84 | if jumpdrive.config.emerge_uncharted and empty_msg == "uncharted" then 85 | local callback = function(_, _, calls_remaining, _) 86 | if calls_remaining == 0 then 87 | core.chat_send_player(playername, "Charting complete!") 88 | end 89 | end 90 | core.emerge_area(target_pos1, target_pos2, callback) 91 | end 92 | msg = msg .. "\nJump-target is " .. empty_msg 93 | success = false 94 | end 95 | 96 | -- check preflight conditions 97 | local preflight_result = jumpdrive.preflight_check(pos, targetPos, radius, playername) 98 | 99 | if not preflight_result.success then 100 | -- check failed in customization 101 | msg = msg .. "\nPreflight check failed!" 102 | if preflight_result.message then 103 | msg = preflight_result.message 104 | end 105 | success = false 106 | end 107 | 108 | local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos) 109 | local powerstorage = meta:get_int("powerstorage") 110 | 111 | if powerstorage < power_req then 112 | -- not enough power 113 | msg = msg .. "\nNot enough power: required=" .. math.floor(power_req) .. ", actual: " .. powerstorage .. " EU" 114 | success = false 115 | end 116 | 117 | return success, msg 118 | end 119 | 120 | 121 | 122 | -- execute jump 123 | jumpdrive.execute_jump = function(pos, player) 124 | 125 | local meta = minetest.get_meta(pos) 126 | 127 | local radius = jumpdrive.get_radius(pos) 128 | local targetPos = jumpdrive.get_meta_pos(pos) 129 | 130 | local distance = vector.distance(pos, targetPos) 131 | local power_req = jumpdrive.calculate_power(radius, distance, pos, targetPos) 132 | 133 | local radius_vector = {x=radius, y=radius, z=radius} 134 | local source_pos1 = vector.subtract(pos, radius_vector) 135 | local source_pos2 = vector.add(pos, radius_vector) 136 | local target_pos1 = vector.subtract(targetPos, radius_vector) 137 | local target_pos2 = vector.add(targetPos, radius_vector) 138 | 139 | local success, msg = jumpdrive.simulate_jump(pos, player, false) 140 | if not success then 141 | return false, msg 142 | end 143 | 144 | -- consume power from storage 145 | local powerstorage = meta:get_int("powerstorage") 146 | meta:set_int("powerstorage", powerstorage - power_req) 147 | 148 | local t0 = minetest.get_us_time() 149 | 150 | minetest.sound_play("jumpdrive_engine", { 151 | pos = pos, 152 | max_hear_distance = 50, 153 | gain = 0.7, 154 | }) 155 | 156 | -- actual move 157 | jumpdrive.move(source_pos1, source_pos2, target_pos1, target_pos2) 158 | 159 | local t1 = minetest.get_us_time() 160 | local time_micros = t1 - t0 161 | 162 | minetest.log("action", "[jumpdrive] jump took " .. time_micros .. " us") 163 | 164 | -- show animation in source 165 | minetest.add_particlespawner({ 166 | amount = 200, 167 | time = 2, 168 | minpos = source_pos1, 169 | maxpos = source_pos2, 170 | minvel = {x = -2, y = -2, z = -2}, 171 | maxvel = {x = 2, y = 2, z = 2}, 172 | minacc = {x = -3, y = -3, z = -3}, 173 | maxacc = {x = 3, y = 3, z = 3}, 174 | minexptime = 0.1, 175 | maxexptime = 5, 176 | minsize = 1, 177 | maxsize = 1, 178 | texture = "spark.png", 179 | glow = 5, 180 | }) 181 | 182 | 183 | -- show animation in target 184 | minetest.add_particlespawner({ 185 | amount = 200, 186 | time = 2, 187 | minpos = target_pos1, 188 | maxpos = target_pos2, 189 | minvel = {x = -2, y = -2, z = -2}, 190 | maxvel = {x = 2, y = 2, z = 2}, 191 | minacc = {x = -3, y = -3, z = -3}, 192 | maxacc = {x = 3, y = 3, z = 3}, 193 | minexptime = 0.1, 194 | maxexptime = 5, 195 | minsize = 1, 196 | maxsize = 1, 197 | texture = "spark.png", 198 | glow = 5, 199 | }) 200 | 201 | return true, time_micros 202 | end 203 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 Thomas Rudin / Buckaroo Banzay 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /mapgen.lua: -------------------------------------------------------------------------------- 1 | local metric_mapgen_events_count 2 | 3 | 4 | if minetest.get_modpath("monitoring") then 5 | metric_mapgen_events_count = monitoring.gauge( 6 | "jumpdrive_mapgen_events_count", 7 | "number of events in the mapgen cache" 8 | ) 9 | end 10 | 11 | local events = {} -- list of {minp, maxp, time} 12 | jumpdrive.mapgen = {} 13 | 14 | jumpdrive.mapgen.reset = function() 15 | events = {} 16 | end 17 | 18 | -- update last mapgen event time 19 | minetest.register_on_generated(function(minp, maxp) 20 | table.insert(events, { 21 | minp = minp, 22 | maxp = maxp, 23 | time = minetest.get_us_time() 24 | }) 25 | end) 26 | 27 | 28 | -- cleanup 29 | local timer = 0 30 | minetest.register_globalstep(function(dtime) 31 | timer = timer + dtime 32 | if timer < 5 then return end 33 | timer=0 34 | 35 | local time = minetest.get_us_time() 36 | local delay_seconds = 10 37 | 38 | local copied_events = events 39 | events = {} 40 | 41 | local count = 0 42 | for _, event in ipairs(copied_events) do 43 | if event.time > (time - (delay_seconds * 1000000)) then 44 | -- still recent 45 | table.insert(events, event) 46 | count = count + 1 47 | end 48 | end 49 | 50 | if metric_mapgen_events_count then 51 | metric_mapgen_events_count.set(count) 52 | end 53 | 54 | end) 55 | 56 | 57 | -- any number = mapgen recently active in that area 58 | jumpdrive.check_mapgen = function(pos) 59 | for _, event in ipairs(events) do 60 | local mapgen_distance = vector.distance(pos, event.minp) 61 | if mapgen_distance < 200 then 62 | return mapgen_distance 63 | end 64 | end 65 | 66 | return false 67 | end 68 | -------------------------------------------------------------------------------- /metrics.lua: -------------------------------------------------------------------------------- 1 | local metric = monitoring.counter("jumpdrive_move_calls", "number of jumpdrive.move calls") 2 | local metric_time = monitoring.counter("jumpdrive_move_time", "time usage in microseconds for jumpdrive.move calls") 3 | 4 | jumpdrive.move = metric.wrap(metric_time.wrap(jumpdrive.move)) 5 | -------------------------------------------------------------------------------- /migrate.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | jumpdrive.migrate_engine_meta = function(pos, meta) 4 | 5 | -- previous version had no such variable in the metadata 6 | local max_store = meta:get_int("max_powerstorage") 7 | if max_store == 0 then 8 | meta:set_int("max_powerstorage", jumpdrive.config.powerstorage) 9 | end 10 | 11 | local power_requirement = meta:get_int("power_requirement") 12 | if power_requirement == 0 then 13 | meta:set_int("power_requirement", jumpdrive.config.powerrequirement) 14 | end 15 | 16 | -- start nodetimer if not started 17 | local timer = minetest.get_node_timer(pos) 18 | if not timer:is_started() then 19 | timer:start(2) 20 | end 21 | 22 | -- inventories 23 | local inv = meta:get_inventory() 24 | inv:set_size("main", 8) 25 | inv:set_size("upgrade", 4) 26 | 27 | end 28 | -------------------------------------------------------------------------------- /mod.conf: -------------------------------------------------------------------------------- 1 | name = jumpdrive 2 | optional_depends = """ 3 | mesecons, 4 | travelnet, 5 | telemosaic, 6 | elevator, 7 | vacuum, 8 | locator, 9 | areas, 10 | protector, 11 | digilines, 12 | display_api, 13 | pipeworks, 14 | monitoring, 15 | beds, 16 | ropes, 17 | sethome, 18 | default, 19 | technic, 20 | drawers, 21 | textline, 22 | player_monoids, 23 | planet_mars, 24 | vizlib, 25 | mcl_sounds, 26 | mcl_formspec, 27 | mtt 28 | """ -------------------------------------------------------------------------------- /move/move.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -- moves the source to the target area 7 | -- no protection- or overlap checking is done here 8 | function jumpdrive.move(source_pos1, source_pos2, target_pos1, target_pos2) 9 | 10 | minetest.log("action", "[jumpdrive] initiating jump (" .. 11 | minetest.pos_to_string(source_pos1) .. "-" .. minetest.pos_to_string(source_pos2) .. 12 | ") (" .. minetest.pos_to_string(target_pos1) .. "-" .. minetest.pos_to_string(target_pos2) .. ")") 13 | 14 | -- step 1: copy via voxel manip 15 | -- https://dev.minetest.net/VoxelManip#Examples 16 | 17 | -- delta between source and target 18 | local delta_vector = vector.subtract(target_pos1, source_pos1) 19 | 20 | -- center of source 21 | local source_center = vector.add(source_pos1, vector.divide(vector.subtract(source_pos2, source_pos1), 2)) 22 | minetest.log("action", "[jumpdrive] source-center: " .. minetest.pos_to_string(source_center)) 23 | 24 | local t0 = minetest.get_us_time() 25 | 26 | -- load areas (just a precaution) 27 | if minetest.load_area then 28 | minetest.load_area(source_pos1, source_pos2) 29 | minetest.load_area(target_pos1, target_pos2) 30 | end 31 | 32 | -- move mapdata (nodeids, param1, param2) 33 | local movenode_list = jumpdrive.move_mapdata(source_pos1, source_pos2, target_pos1, target_pos2) 34 | 35 | local t1 = minetest.get_us_time() 36 | minetest.log("action", "[jumpdrive] step I took " .. (t1 - t0) .. " us") 37 | 38 | -- step 2: check meta/timers and copy if needed 39 | t0 = minetest.get_us_time() 40 | jumpdrive.move_metadata(source_pos1, source_pos2, delta_vector) 41 | jumpdrive.move_nodetimers(source_pos1, source_pos2, delta_vector) 42 | 43 | -- move "on_movenode" aware nodes 44 | for _, entry in ipairs(movenode_list) do 45 | entry.nodedef.on_movenode(entry.from_pos, entry.to_pos, { 46 | edge = entry.edge 47 | }) 48 | end 49 | 50 | -- print stats 51 | t1 = minetest.get_us_time() 52 | minetest.log("action", "[jumpdrive] step II took " .. (t1 - t0) .. " us") 53 | 54 | 55 | -- step 4: move objects 56 | t0 = minetest.get_us_time() 57 | jumpdrive.move_objects(source_center, source_pos1, source_pos2, delta_vector) 58 | 59 | -- move players 60 | jumpdrive.move_players(source_pos1, source_pos2, delta_vector) 61 | 62 | t1 = minetest.get_us_time() 63 | minetest.log("action", "[jumpdrive] step IV took " .. (t1 - t0) .. " us") 64 | 65 | 66 | -- step 5: clear source area with voxel manip 67 | t0 = minetest.get_us_time() 68 | jumpdrive.clear_area(source_pos1, source_pos2) 69 | 70 | t1 = minetest.get_us_time() 71 | minetest.log("action", "[jumpdrive] step V took " .. (t1 - t0) .. " us") 72 | 73 | -- call after_jump callbacks 74 | jumpdrive.fire_after_jump({ 75 | pos1 = source_pos1, 76 | pos2 = source_pos2 77 | }, { 78 | pos1 = target_pos1, 79 | pos2 = target_pos2 80 | }) 81 | 82 | end 83 | -------------------------------------------------------------------------------- /move/move_mapdata.lua: -------------------------------------------------------------------------------- 1 | local c_air = minetest.get_content_id("air") 2 | 3 | -- map of replaced content id's on jump 4 | -- TODO: expose as api function 5 | -- = 6 | local mapped_content_ids = {} 7 | 8 | if minetest.get_modpath("vacuum") then 9 | -- don't jump vacuum 10 | mapped_content_ids[minetest.get_content_id("vacuum:vacuum")] = c_air 11 | end 12 | 13 | if minetest.get_modpath("planet_mars") then 14 | -- alias planet_mars:airlight to air 15 | mapped_content_ids[minetest.get_content_id("planet_mars:airlight")] = c_air 16 | end 17 | 18 | -- map of "on_movenode" aware node id's 19 | -- content_id = nodedef 20 | local movenode_aware_nodeids = {} 21 | 22 | -- collect movenode aware node id's 23 | minetest.register_on_mods_loaded(function() 24 | local count = 0 25 | for nodename, nodedef in pairs(minetest.registered_nodes) do 26 | if type(nodedef.on_movenode) == "function" then 27 | count = count + 1 28 | local id = minetest.get_content_id(nodename) 29 | movenode_aware_nodeids[id] = nodedef 30 | end 31 | end 32 | minetest.log("action", "[jumpdrive] collected " .. count .. " 'on_movenode' aware nodes") 33 | end) 34 | 35 | function jumpdrive.move_mapdata(source_pos1, source_pos2, target_pos1, target_pos2) 36 | 37 | -- delta between source and target 38 | local delta_vector = vector.subtract(target_pos1, source_pos1) 39 | 40 | -- read source 41 | local manip = minetest.get_voxel_manip() 42 | local e1, e2 = manip:read_from_map(source_pos1, source_pos2) 43 | local source_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2}) 44 | local source_data = manip:get_data() 45 | local source_param1 = manip:get_light_data() 46 | local source_param2 = manip:get_param2_data() 47 | 48 | minetest.log("action", "[jumpdrive] read source-data") 49 | 50 | -- write target 51 | manip = minetest.get_voxel_manip() 52 | e1, e2 = manip:read_from_map(target_pos1, target_pos2) 53 | local target_area = VoxelArea:new({MinEdge=e1, MaxEdge=e2}) 54 | local target_data = manip:get_data() 55 | local target_param1 = manip:get_light_data() 56 | local target_param2 = manip:get_param2_data() 57 | 58 | -- list of { from_pos, to_pos, } 59 | local movenode_list = {} 60 | 61 | minetest.log("action", "[jumpdrive] read target-data"); 62 | 63 | for z=source_pos1.z, source_pos2.z do 64 | for y=source_pos1.y, source_pos2.y do 65 | for x=source_pos1.x, source_pos2.x do 66 | 67 | local from_pos = { x=x, y=y, z=z } 68 | local to_pos = vector.add(from_pos, delta_vector) 69 | 70 | local source_index = source_area:indexp(from_pos) 71 | local target_index = target_area:indexp(to_pos) 72 | 73 | -- copy block id 74 | local id = source_data[source_index] 75 | 76 | if mapped_content_ids[id] then 77 | -- replace original content id 78 | id = mapped_content_ids[id] 79 | end 80 | 81 | target_data[target_index] = id 82 | 83 | if movenode_aware_nodeids[id] then 84 | 85 | -- check if we are on an edge 86 | local edge = { x=0, y=0, z=0 } 87 | 88 | -- negative edge 89 | if source_pos1.x == x then edge.x = -1 end 90 | if source_pos1.y == y then edge.y = -1 end 91 | if source_pos1.z == z then edge.z = -1 end 92 | -- positive edge 93 | if source_pos2.z == x then edge.x = 1 end 94 | if source_pos2.y == y then edge.y = 1 end 95 | if source_pos2.z == z then edge.z = 1 end 96 | 97 | table.insert(movenode_list, { 98 | from_pos = from_pos, 99 | to_pos = to_pos, 100 | edge = edge, 101 | nodedef = movenode_aware_nodeids[id] 102 | }) 103 | end 104 | 105 | -- copy params 106 | target_param1[target_index] = source_param1[source_index] 107 | target_param2[target_index] = source_param2[source_index] 108 | end 109 | end 110 | end 111 | 112 | 113 | manip:set_data(target_data) 114 | manip:set_light_data(target_param1) 115 | manip:set_param2_data(target_param2) 116 | manip:write_to_map() 117 | manip:update_map() 118 | 119 | return movenode_list 120 | end 121 | -------------------------------------------------------------------------------- /move/move_metadata.lua: -------------------------------------------------------------------------------- 1 | 2 | -- invoked from move.lua 3 | jumpdrive.move_metadata = function(source_pos1, source_pos2, delta_vector) 4 | 5 | 6 | local target_pos1 = vector.add(source_pos1, delta_vector) 7 | local target_pos2 = vector.add(source_pos2, delta_vector) 8 | 9 | -- check if there is some "stale" metadata in the target area 10 | local target_meta_pos_list = minetest.find_nodes_with_meta(target_pos1, target_pos2) 11 | for _,target_pos in pairs(target_meta_pos_list) do 12 | minetest.log("warning", "[jumpdrive] clearing spurious meta in " .. minetest.pos_to_string(target_pos)) 13 | local target_meta = minetest.get_meta(target_pos) 14 | target_meta:from_table(nil) 15 | end 16 | 17 | local meta_pos_list = minetest.find_nodes_with_meta(source_pos1, source_pos2) 18 | for _,source_pos in pairs(meta_pos_list) do 19 | local target_pos = vector.add(source_pos, delta_vector) 20 | 21 | local source_meta = minetest.get_meta(source_pos) 22 | local source_table = source_meta:to_table() 23 | 24 | -- copy metadata to target 25 | minetest.get_meta(target_pos):from_table(source_table) 26 | 27 | local node = minetest.get_node(source_pos) 28 | 29 | jumpdrive.node_compat(node.name, source_pos, target_pos, source_pos1, source_pos2, delta_vector) 30 | 31 | -- clear metadata in source 32 | source_meta:from_table(nil) 33 | end 34 | 35 | jumpdrive.commit_node_compat() 36 | 37 | end 38 | -------------------------------------------------------------------------------- /move/move_nodetimers.lua: -------------------------------------------------------------------------------- 1 | 2 | -- collect nodes with on_timer attributes 3 | local node_names_with_timer = {} 4 | minetest.after(4, function() 5 | for _,node in pairs(minetest.registered_nodes) do 6 | if node.on_timer then 7 | table.insert(node_names_with_timer, node.name) 8 | end 9 | end 10 | minetest.log("action", "[jumpdrive] collected " .. #node_names_with_timer .. " items with node timers") 11 | end) 12 | 13 | 14 | -- invoked from move.lua 15 | jumpdrive.move_nodetimers = function(source_pos1, source_pos2, delta_vector) 16 | 17 | if #node_names_with_timer == 0 then 18 | -- no node timer-nodes or not collected yet 19 | return 20 | end 21 | 22 | local list = minetest.find_nodes_in_area(source_pos1, source_pos2, node_names_with_timer) 23 | 24 | for _,source_pos in pairs(list) do 25 | local target_pos = vector.add(source_pos, delta_vector) 26 | 27 | local source_timer = minetest.get_node_timer(source_pos) 28 | local target_timer = minetest.get_node_timer(target_pos) 29 | 30 | if source_timer:is_started() then 31 | -- set target timer 32 | target_timer:set( 33 | source_timer:get_timeout(), 34 | source_timer:get_elapsed() 35 | ) 36 | 37 | -- clear source timer 38 | source_timer:stop() 39 | end 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /move/move_objects.lua: -------------------------------------------------------------------------------- 1 | 2 | -- invoked from move.lua 3 | jumpdrive.move_objects = function(source_center, source_pos1, source_pos2, delta_vector) 4 | 5 | local margin = vector.new(0.5, 0.5, 0.5) 6 | local pos1 = vector.subtract(source_pos1, margin) 7 | local pos2 = vector.add(source_pos2, margin) 8 | 9 | local radius = math.ceil(vector.distance(source_center, pos2)) 10 | 11 | local all_objects = minetest.get_objects_inside_radius(source_center, radius); 12 | for _,obj in ipairs(all_objects) do 13 | 14 | local objPos = obj:get_pos() 15 | 16 | local x_match = objPos.x >= pos1.x and objPos.x <= pos2.x 17 | local y_match = objPos.y >= pos1.y and objPos.y <= pos2.y 18 | local z_match = objPos.z >= pos1.z and objPos.z <= pos2.z 19 | 20 | if x_match and y_match and z_match and not obj:is_player() then 21 | minetest.log("action", "[jumpdrive] object: @ " .. minetest.pos_to_string(objPos)) 22 | 23 | -- coords in range 24 | local entity = obj:get_luaentity() 25 | 26 | if not entity then 27 | minetest.log("action", "[jumpdrive] moving object") 28 | obj:set_pos( vector.add(objPos, delta_vector) ) 29 | 30 | elseif entity.name:find("^mobs_animal:") then 31 | minetest.log("action", "[jumpdrive] moving animal") 32 | obj:set_pos( vector.add(objPos, delta_vector) ) 33 | 34 | elseif entity.name == "__builtin:item" then 35 | minetest.log("action", "[jumpdrive] moving dropped item") 36 | obj:set_pos( vector.add(objPos, delta_vector) ) 37 | 38 | else 39 | minetest.log("action", "[jumpdrive] removing entity: " .. entity.name) 40 | obj:remove() 41 | 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /move/move_players.lua: -------------------------------------------------------------------------------- 1 | local use_player_monoids = minetest.global_exists("player_monoids") 2 | 3 | function jumpdrive.move_players(source_pos1, source_pos2, delta_vector) 4 | -- move players 5 | for _,player in ipairs(minetest.get_connected_players()) do 6 | local playerPos = player:get_pos() 7 | local player_name = player:get_player_name() 8 | 9 | local xMatch = playerPos.x >= (source_pos1.x-0.5) and playerPos.x <= (source_pos2.x+0.5) 10 | local yMatch = playerPos.y >= (source_pos1.y-0.5) and playerPos.y <= (source_pos2.y+0.5) 11 | local zMatch = playerPos.z >= (source_pos1.z-0.5) and playerPos.z <= (source_pos2.z+0.5) 12 | 13 | if xMatch and yMatch and zMatch and player:is_player() then 14 | minetest.log("action", "[jumpdrive] moving player: " .. player:get_player_name()) 15 | 16 | -- override gravity if "player_monoids" is available 17 | -- *should* execute before the player get moved 18 | -- to prevent falling through not yet loaded blocks 19 | if use_player_monoids then 20 | -- modify gravity 21 | player_monoids.gravity:add_change(player, 0.01, "jumpdrive:gravity") 22 | 23 | minetest.after(3.0, function() 24 | -- restore gravity 25 | local player_deferred = minetest.get_player_by_name(player_name) 26 | if player_deferred then 27 | player_monoids.gravity:del_change(player_deferred, "jumpdrive:gravity") 28 | end 29 | end) 30 | end 31 | 32 | local new_player_pos = vector.add(playerPos, delta_vector) 33 | player:set_pos( new_player_pos ); 34 | 35 | -- send moved mapblock to player 36 | if player.send_mapblock and type(player.send_mapblock) == "function" then 37 | player:send_mapblock(jumpdrive.get_mapblock_from_pos(new_player_pos)) 38 | end 39 | end 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /mtt.lua: -------------------------------------------------------------------------------- 1 | local pos1 = { x=-50, y=-10, z=-50 } 2 | local pos2 = { x=50, y=50, z=50 } 3 | 4 | mtt.emerge_area(pos1, pos2) 5 | 6 | mtt.register("basic move-test", function(callback) 7 | local source_pos1 = { x=0, y=0, z=0 } 8 | local source_pos2 = { x=5, y=5, z=5 } 9 | local target_pos1 = { x=10, y=10, z=10 } 10 | local target_pos2 = { x=15, y=15, z=15 } 11 | 12 | minetest.get_voxel_manip(source_pos1, source_pos1) 13 | local src_node = minetest.get_node(source_pos1) 14 | 15 | areas:add("dummy", "landscape", source_pos1, source_pos2) 16 | areas:save() 17 | 18 | assert(not minetest.is_protected(source_pos1, "dummy")) 19 | assert(minetest.is_protected(source_pos1, "dummy2")) 20 | 21 | jumpdrive.move(source_pos1, source_pos2, target_pos1, target_pos2) 22 | 23 | assert(not minetest.is_protected(source_pos1, "dummy")) 24 | assert(not minetest.is_protected(source_pos1, "dummy2")) 25 | 26 | assert(not minetest.is_protected(target_pos1, "dummy")) 27 | assert(minetest.is_protected(target_pos1, "dummy2")) 28 | 29 | minetest.get_voxel_manip(target_pos1, target_pos1) 30 | local target_node = minetest.get_node(target_pos1) 31 | 32 | if target_node.name ~= src_node.name then 33 | error("moved node name does not match") 34 | end 35 | 36 | if target_node.param2 ~= src_node.param2 then 37 | error("moved param2 does not match") 38 | end 39 | 40 | callback() 41 | end) 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Minetest jumpdrive 2 | ====== 3 | 4 | ![](https://github.com/mt-mods/jumpdrive/workflows/luacheck/badge.svg) 5 | ![](https://github.com/mt-mods/jumpdrive/workflows/integration-test/badge.svg) 6 | 7 | 8 | A simple [Jumpdrive](https://en.wikipedia.org/wiki/Jump_drive) for minetest 9 | 10 | Take your buildings with you on your journey 11 | 12 | * Github: [https://github.com/thomasrudin-mt/jumpdrive](https://github.com/thomasrudin-mt/jumpdrive) 13 | * Forum topic: [https://forum.minetest.net/viewtopic.php?f=9&t=20073](https://forum.minetest.net/viewtopic.php?f=9&t=20073) 14 | 15 | # Operation 16 | 17 | * Place a 'jumpdrive:engine' into the center of your creation. 18 | * Connect the engine to a technic HV network 19 | * Let the engine charge 20 | * Choose your target coordinates (should be air or ignore blocks) 21 | * Select your cube-radius 22 | * Click "show" and check the green (source) and red (target) destination markers if everything is in range 23 | * Click "jump" 24 | 25 | Example setup with technic: 26 | 27 | ![](screenshots/screenshot_20220305_161502.png?raw=true) 28 | 29 | 30 | # Compatibility 31 | 32 | Optional dependencies: 33 | * Mesecon interaction (execute jump on signal) 34 | * Technic rechargeable (HV) 35 | * Travelnet box (gets rewired after jump) 36 | * Elevator (on_place gets called after jump) 37 | * Locator (gets removed and added after each jump) 38 | * Pipeworks teleport tubes (with a patch to pipeworks) 39 | * Beds (thx to @tuedel) 40 | * Ropes (thx to @tuedel) 41 | * Mission-wand as coordinate bookmark (thx to @SwissalpS) 42 | * Compass as coordinate bookmark (thx to @SwissalpS) 43 | * Areas 44 | * Drawers 45 | 46 | # Fuel 47 | 48 | The engine can be connected to a technic HV network or fuelled with power items. 49 | Power items are one of the following 50 | * `default:mese_crystal_fragment` 51 | * `default:mese_crystal` 52 | * `default:mese` 53 | 54 | # Energy requirements 55 | 56 | The energy requirements formula looks like this: **10 x radius x distance** 57 | 58 | For example: 59 | * Distance: 100 blocks 60 | * Radius: 5 blocks 61 | * Required energy: 10 x 5 x 100 = 5000 62 | 63 | # Upgrades 64 | 65 | If the `technic` mod is installed the following items can be used in the upgrade slot: 66 | * `technic:red_energy_crystal` increases power storage 67 | * `technic:green_energy_crystal` increases power storage 68 | * `technic:blue_energy_crystal` increases power storage 69 | * `technic:control_logic_unit` increases power recharge rate 70 | 71 | # Protection 72 | 73 | The source and destination areas are checked for protection so you can't remove and jump into someone else's buildings. 74 | 75 | 76 | # Screenshots 77 | 78 | Interface: 79 | 80 | ![](screenshots/screenshot_20180507_200309.png?raw=true) 81 | 82 | Example: 83 | 84 | ![](screenshots/screenshot_20180507_200203.png?raw=true) 85 | 86 | # Advanced operation 87 | 88 | ## Coordinate bookmarking 89 | 90 | You can place empty books into the drive inventory and write the coordinates to them with the "Write to book" button. 91 | The "Read from bookmark" button reads the coordinates from the next valid bookmark item in the inventory. From right to left. 92 | A used bookmark item is placed in the first free slot from the left. 93 | Bookmark items are: 94 | * Written books saved by jumpdrive (or correctly by hand) 95 | * Mission position wands 96 | * Compasses 97 | 98 | ## Diglines 99 | 100 | * See: [Digilines](doc/digiline.md) 101 | 102 | # Settings 103 | 104 | Settings in minetest.conf: 105 | 106 | * **jumpdrive.max_radius** max radius of the jumpdrive (default: *15*) 107 | * **jumpdrive.max_area_radius** max radius of the area jumpdrive (default: *25*) 108 | * **jumpdrive.powerstorage** power storage of the drive (default: *1000000*) 109 | * **jumpdrive.power_requirement** power requirement for charging (default: *2500*) 110 | 111 | # Lua api 112 | 113 | ## Preflight check 114 | 115 | The preflight check can be overriden to execute additional checks: 116 | 117 | ```lua 118 | jumpdrive.preflight_check = function(source, destination, radius, player) 119 | -- check for height limit, only space travel allowed 120 | if destination.y < 1000 then 121 | return { success=false, message="Atmospheric travel not allowed!" } 122 | end 123 | 124 | -- everything ok 125 | return { success=true } 126 | end 127 | ``` 128 | 129 | ## Fuel calc 130 | 131 | The default fuel calc can be overwritten by a depending mod: 132 | 133 | ```lua 134 | -- calculates the power requirements for a jump 135 | jumpdrive.calculate_power = function(radius, distance, sourcePos, targetPos) 136 | return 10 * distance * radius 137 | end 138 | ``` 139 | 140 | ## Movenode compatibility 141 | 142 | Nodes can be made aware of a changing position if they implement a `on_movenode` function 143 | on the node-definition: 144 | 145 | ```lua 146 | -- example with an override 147 | minetest.override_item("travelnet:travelnet", { 148 | on_movenode = function(from_pos, to_pos, additional_info) 149 | -- additional_info = { edge = { x=0, y=0, z=0 } } 150 | -- magic! 151 | end 152 | }) 153 | ``` 154 | 155 | * `additional_info.edge` is the vector to the nearest edge if any 156 | 157 | 158 | ## Hooks 159 | 160 | ```lua 161 | -- register a callback that is called upon jump completion 162 | -- can also be used if the `on_movenode` above needs a kind of "commit" to write the changed state to files 163 | jumpdrive.register_after_jump(function(from_area, to_area) 164 | -- from_area/to_area = { pos1, pos2 } 165 | end) 166 | ``` 167 | 168 | 169 | # Sources 170 | 171 | * jumprive_engine.ogg: https://freesound.org/people/kaboose102/sounds/340257/ 172 | 173 | # Contributors 174 | 175 | * @tuedel 176 | * @SwissalpS 177 | * @Panquesito7 178 | * @OgelGames 179 | * @S-S-X 180 | * Jeremy#2233 181 | * Purple#2916 182 | 183 | # License 184 | * Code: `MIT` 185 | * Textures `CC BY-SA 4.0` 186 | 187 | # Attributions 188 | * `textures/jumpdrive.png`/`textures/jumpdrive_backbone.png`/`textures/jumpdrive_fleet_controller.png`/`textures/jumpdrive_warpdevice.png` 189 | * Jeremy#2233 / Purple#2916 190 | -------------------------------------------------------------------------------- /screenshots/screenshot_20180507_200203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/screenshots/screenshot_20180507_200203.png -------------------------------------------------------------------------------- /screenshots/screenshot_20180507_200309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/screenshots/screenshot_20180507_200309.png -------------------------------------------------------------------------------- /screenshots/screenshot_20220305_161502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/screenshots/screenshot_20220305_161502.png -------------------------------------------------------------------------------- /settingtypes.txt: -------------------------------------------------------------------------------- 1 | # Allow jumpdrives to generate ungenerated areas 2 | jumpdrive.allow_emerge (Chart uncharted jump targets) bool false 3 | -------------------------------------------------------------------------------- /sounds/jumpdrive_engine.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/sounds/jumpdrive_engine.ogg -------------------------------------------------------------------------------- /technic_run.lua: -------------------------------------------------------------------------------- 1 | 2 | jumpdrive.technic_run = function(pos) 3 | local meta = minetest.get_meta(pos) 4 | jumpdrive.migrate_engine_meta(pos, meta) 5 | 6 | local eu_input = meta:get_int("HV_EU_input") 7 | local store = meta:get_int("powerstorage") 8 | local power_requirement = meta:get_int("power_requirement") 9 | local max_store = meta:get_int("max_powerstorage") 10 | 11 | if store < max_store then 12 | -- charge 13 | meta:set_int("HV_EU_demand", power_requirement) 14 | store = store + eu_input 15 | meta:set_int("powerstorage", math.min(store, max_store)) 16 | else 17 | -- charged 18 | meta:set_int("HV_EU_demand", 0) 19 | end 20 | 21 | jumpdrive.update_infotext(meta, pos) 22 | end 23 | -------------------------------------------------------------------------------- /testship.we: -------------------------------------------------------------------------------- 1 | 5:return {{["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 1, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 1, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 2, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 2, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 3, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 3, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 4, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 4, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 5, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 5, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 6, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 6, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 7, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 7, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 8, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 8, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 9, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 9, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 0, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 1, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 2, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 1, ["param1"] = 78, ["z"] = 2, ["name"] = "technic:hv_cable"}, {["x"] = 3, ["meta"] = {["fields"] = {["infotext"] = "Arrayed Solar HV Generator Active (4900 EU)", ["HV_EU_supply"] = "4900"}, ["inventory"] = {}}, ["y"] = 2, ["param1"] = 94, ["z"] = 2, ["name"] = "technic:solar_array_hv"}, {["x"] = 3, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 3, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 1, ["param1"] = 94, ["z"] = 2, ["name"] = "technic:hv_cable"}, {["x"] = 4, ["meta"] = {["fields"] = {["infotext"] = "Arrayed Solar HV Generator Active (4900 EU)", ["HV_EU_supply"] = "4900"}, ["inventory"] = {}}, ["y"] = 2, ["param1"] = 110, ["z"] = 2, ["name"] = "technic:solar_array_hv"}, {["x"] = 4, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 4, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 1, ["param1"] = 109, ["z"] = 2, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["y"] = 1, ["param1"] = 126, ["z"] = 3, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["y"] = 1, ["param1"] = 142, ["z"] = 4, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["y"] = 1, ["param1"] = 158, ["z"] = 5, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["meta"] = {["fields"] = {["infotext"] = "Switching Station. Supply: 19.6 kEU Demand: 2500 EU", ["formspec"] = "field[channel;Channel;${channel}]"}, ["inventory"] = {}}, ["y"] = 2, ["z"] = 2, ["name"] = "technic:switching_station"}, {["x"] = 5, ["meta"] = {["fields"] = {["HV_EU_input"] = "8550", ["HV_EU_demand"] = "100000", ["infotext"] = "HV Battery Box: 680.3 kEU / 1000 kEU", ["formspec"] = "size[8,9]image[1,1;1,2;technic_power_meter_bg.png]list[context;src;3,1;1,1;]image[4,1;1,1;technic_battery_reload.png]list[context;dst;5,1;1,1;]label[0,0;HV Battery Box]label[3,0;Charge]label[5,0;Discharge]label[1,3;Power level]list[current_player;main;0,5;8,4;]listring[context;dst]listring[current_player;main]listring[context;src]listring[current_player;main]list[context;upgrade1;3.5,3;1,1;]list[context;upgrade2;4.5,3;1,1;]label[3.5,4;Upgrade Slots]listring[context;upgrade1]listring[current_player;main]listring[context;upgrade2]listring[current_player;main]image[1,1;1,2;technic_power_meter_bg.png^[lowpart:0:technic_power_meter_fg.png]", ["internal_EU_charge"] = "680298", ["internal_EU_charge_max"] = "1000000", ["last_side_shown"] = "6", ["HV_EU_supply"] = "400000"}, ["inventory"] = {["upgrade2"] = {""}, ["src"] = {""}, ["upgrade1"] = {""}, ["dst"] = {""}}}, ["param2"] = 3, ["y"] = 2, ["z"] = 3, ["name"] = "technic:hv_battery_box6"}, {["x"] = 5, ["meta"] = {["fields"] = {["HV_EU_input"] = "8550", ["HV_EU_demand"] = "100000", ["infotext"] = "HV Battery Box: 671.7 kEU / 1000 kEU", ["formspec"] = "size[8,9]image[1,1;1,2;technic_power_meter_bg.png]list[context;src;3,1;1,1;]image[4,1;1,1;technic_battery_reload.png]list[context;dst;5,1;1,1;]label[0,0;HV Battery Box]label[3,0;Charge]label[5,0;Discharge]label[1,3;Power level]list[current_player;main;0,5;8,4;]listring[context;dst]listring[current_player;main]listring[context;src]listring[current_player;main]list[context;upgrade1;3.5,3;1,1;]list[context;upgrade2;4.5,3;1,1;]label[3.5,4;Upgrade Slots]listring[context;upgrade1]listring[current_player;main]listring[context;upgrade2]listring[current_player;main]image[1,1;1,2;technic_power_meter_bg.png^[lowpart:0:technic_power_meter_fg.png]", ["internal_EU_charge"] = "680298", ["internal_EU_charge_max"] = "1000000", ["last_side_shown"] = "6", ["HV_EU_supply"] = "400000"}, ["inventory"] = {["upgrade2"] = {""}, ["src"] = {""}, ["upgrade1"] = {""}, ["dst"] = {""}}}, ["param2"] = 3, ["y"] = 2, ["z"] = 4, ["name"] = "technic:hv_battery_box6"}, {["x"] = 5, ["y"] = 2, ["param1"] = 174, ["z"] = 5, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["y"] = 3, ["param1"] = 190, ["z"] = 5, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["y"] = 4, ["param1"] = 206, ["z"] = 5, ["name"] = "technic:hv_cable"}, {["x"] = 5, ["meta"] = {["fields"] = {["power_requirement"] = "2500", ["HV_EU_demand"] = "2500", ["formspec"] = "size[8,10.55;]field[0.3,0.5;2,1;x;X;-148]field[2.3,0.5;2,1;y;Y;38]field[4.3,0.5;2,1;z;Z;275]field[6.3,0.5;2,1;radius;Radius;5]button_exit[0,1;2,1;jump;Jump]button_exit[2,1;2,1;show;Show]button_exit[4,1;2,1;save;Save]button[6,1;2,1;reset;Reset]button[0,2;4,1;write_book;Write to book]button[4,2;4,1;read_book;Read from bookmark]list[context;main;0,3.25;8,1;]list[current_player;main;0,5.75;8,4;]field[4.3,10.27;3.2,1;digiline_channel;Digiline channel;]button_exit[7,9.95;1,1;set_digiline_channel;Set]listring[context;main]listring[current_player;main]label[3,4.7;Upgrades]list[context;upgrade;4,4.5;4,1;]", ["y"] = "38", ["x"] = "-148", ["owner"] = "singleplayer", ["channel"] = "jumpdrive", ["HV_EU_input"] = "2500", ["z"] = "275", ["infotext"] = "Power: 2500/2500 Store: 202500/1000000", ["max_powerstorage"] = "1000000", ["radius"] = "5", ["powerstorage"] = "202500"}, ["inventory"] = {["main"] = {"", "", "", "", "", "", "", ""}, ["upgrade"] = {"", "", "", ""}}}, ["y"] = 5, ["z"] = 5, ["name"] = "jumpdrive:engine"}, {["x"] = 5, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 5, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 1, ["param1"] = 94, ["z"] = 2, ["name"] = "technic:hv_cable"}, {["x"] = 6, ["meta"] = {["fields"] = {["infotext"] = "Arrayed Solar HV Generator Active (4900 EU)", ["HV_EU_supply"] = "4900"}, ["inventory"] = {}}, ["y"] = 2, ["param1"] = 110, ["z"] = 2, ["name"] = "technic:solar_array_hv"}, {["x"] = 6, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 6, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 1, ["param1"] = 78, ["z"] = 2, ["name"] = "technic:hv_cable"}, {["x"] = 7, ["meta"] = {["fields"] = {["infotext"] = "Arrayed Solar HV Generator Active (4900 EU)", ["HV_EU_supply"] = "4900"}, ["inventory"] = {}}, ["y"] = 2, ["param1"] = 94, ["z"] = 2, ["name"] = "technic:solar_array_hv"}, {["x"] = 7, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 7, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 8, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 9, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 0, ["param1"] = 15, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 1, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 1, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 2, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 2, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 3, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 3, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 4, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 4, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 5, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 5, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 6, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 6, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 7, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 7, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 8, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 8, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 9, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 9, ["z"] = 10, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 0, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 1, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 2, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 3, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 4, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 5, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 6, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 7, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 8, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 9, ["name"] = "default:stone"}, {["x"] = 10, ["y"] = 10, ["z"] = 10, ["name"] = "default:stone"}} -------------------------------------------------------------------------------- /textures/jumpdrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive.png -------------------------------------------------------------------------------- /textures/jumpdrive_air.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_air.png -------------------------------------------------------------------------------- /textures/jumpdrive_area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_area.png -------------------------------------------------------------------------------- /textures/jumpdrive_backbone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_backbone.png -------------------------------------------------------------------------------- /textures/jumpdrive_blue_mese_crystal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_blue_mese_crystal.png -------------------------------------------------------------------------------- /textures/jumpdrive_fleet_controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_fleet_controller.png -------------------------------------------------------------------------------- /textures/jumpdrive_raw_blue_mese_crystal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_raw_blue_mese_crystal.png -------------------------------------------------------------------------------- /textures/jumpdrive_remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_remote.png -------------------------------------------------------------------------------- /textures/jumpdrive_warpdevice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/jumpdrive_warpdevice.png -------------------------------------------------------------------------------- /textures/marker_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/marker_blue.png -------------------------------------------------------------------------------- /textures/marker_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/marker_green.png -------------------------------------------------------------------------------- /textures/marker_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/marker_red.png -------------------------------------------------------------------------------- /textures/spark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mt-mods/jumpdrive/b65f0ccdb552a470ec18b3d8b4779a7daaa69069/textures/spark.png -------------------------------------------------------------------------------- /upgrade.lua: -------------------------------------------------------------------------------- 1 | 2 | jumpdrive.upgrade = {} 3 | 4 | local upgrades = {} 5 | 6 | jumpdrive.upgrade.register = function(item_name, def) 7 | upgrades[item_name] = def 8 | end 9 | 10 | jumpdrive.upgrade.calculate = function(pos) 11 | local meta = minetest.get_meta(pos) 12 | local inv = meta:get_inventory() 13 | local size = inv:get_size("upgrade") 14 | 15 | -- reset values 16 | meta:set_int("power_requirement", jumpdrive.config.powerrequirement) 17 | meta:set_int("max_powerstorage", jumpdrive.config.powerstorage) 18 | 19 | for i=1,size do 20 | local stack = inv:get_stack("upgrade", i) 21 | if not stack:is_empty() then 22 | local upgrade_def = upgrades[stack:get_name()] 23 | if upgrade_def then 24 | upgrade_def(meta) 25 | end 26 | end 27 | end 28 | 29 | end 30 | 31 | if minetest.get_modpath("technic") then 32 | jumpdrive.upgrade.register("technic:red_energy_crystal", function(meta) 33 | meta:set_int("max_powerstorage", meta:get_int("max_powerstorage") + jumpdrive.config.powerstorage*0.1) 34 | end) 35 | 36 | jumpdrive.upgrade.register("technic:green_energy_crystal", function(meta) 37 | meta:set_int("max_powerstorage", meta:get_int("max_powerstorage") + jumpdrive.config.powerstorage*0.2) 38 | end) 39 | 40 | jumpdrive.upgrade.register("technic:blue_energy_crystal", function(meta) 41 | meta:set_int("max_powerstorage", meta:get_int("max_powerstorage") + jumpdrive.config.powerstorage*0.5) 42 | end) 43 | 44 | jumpdrive.upgrade.register("technic:control_logic_unit", function(meta) 45 | meta:set_int("power_requirement", meta:get_int("power_requirement") + jumpdrive.config.powerrequirement) 46 | end) 47 | end 48 | -------------------------------------------------------------------------------- /warp_device.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | minetest.register_node("jumpdrive:warp_device", { 4 | description = "Warp Device", 5 | 6 | tiles = {"jumpdrive_warpdevice.png"}, 7 | groups = {cracky=5,oddly_breakable_by_hand=1,handy=1,pickaxey=1}, 8 | _mcl_blast_resistance = 2, 9 | _mcl_hardness = 0.9, 10 | sounds = jumpdrive.sounds.node_sound_glass_defaults(), 11 | is_ground_content = false, 12 | light_source = 4 13 | }) 14 | --------------------------------------------------------------------------------