107
README.md
@ -4,99 +4,56 @@
|
||||
|
||||
A Minetest mod for user-generated teleportation pads.
|
||||
|
||||
Version: 0.5.0
|
||||
![screenshot](screenshot.png?raw=true "screenshot")
|
||||
|
||||
License:
|
||||
Code: LGPL 2.1 (see included LICENSE file)
|
||||
Textures: CC-BY-SA (see http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
textures/telemosaic_beacon_protected_overlay.png MIT (https://notabug.org/TenPlus1/protector)
|
||||
## Description
|
||||
|
||||
Report bugs or request help on the forum topic.
|
||||
This is a mod for Minetest. It provides teleportation pads, called "beacons". Unlike other teleportation mods, no menus or GUIs are used; you set the destination with a simple "key" item. There is no tooltip for the destination either, so signs are recommended.
|
||||
|
||||
Description
|
||||
-----------
|
||||
Another difference is the limited default range of the beacons. To increase the range, you need to place "extenders" around the beacon. The extenders come in different colors, allowing the extenders to form a pretty pattern; hence the name "telemosaic".
|
||||
|
||||
This is a mod for MineTest. It provides teleportation pads, called
|
||||
"beacons". Unlike other teleportation mods, no menus or GUIs are used;
|
||||
you set the destination with a simple "key" item. There is no
|
||||
tooltip for the destination either, so signs are recommended.
|
||||
## Notes
|
||||
|
||||
Another difference is the limited default range of the beacons.
|
||||
To increase the range, you need to place "extenders" around the beacon.
|
||||
The extenders come in different colors, allowing the extenders to
|
||||
form a pretty pattern; hence the name "telemosaic".
|
||||
This is a maintained fork of https://github.com/bendeutsch/minetest-telemosaic with various improvements and changes, optimized for multiplayer environments.
|
||||
|
||||
Current behavior
|
||||
----------------
|
||||
* Beacons no longer teleport players automatically, they must be right-clicked by a player.
|
||||
* More chat messages are sent to inform and help players with non-functional telemosaics.
|
||||
* Telemosaic keys can be preserved by holding shift when right-clicking a beacon.
|
||||
* Beacons can be enabled and disabled by punching them with a telemosaic key.
|
||||
* If the `digilines` mod is installed, beacons can be controlled by digilines.
|
||||
* and other small changes...
|
||||
|
||||
Beacons are created with 2 diamonds, 3 obsidian blocks, and a wooden
|
||||
door: first row diamond, door, diamond; second row the obsidian blocks.
|
||||
## Usage
|
||||
|
||||
Right-clicking a beacon with a default mese crystal fragment remembers
|
||||
the position in the fragment, which turns into a telemosaic key.
|
||||
Right-clicking a second beacon with the key sets up a teleportation
|
||||
route from the second beacon to the first beacon. To set up a return
|
||||
path, right-click the second beacon with the fragment, and the first
|
||||
beacon with the resulting key again.
|
||||
Right-clicking a beacon with a default mese crystal fragment creates a telemosaic key with the position of the beacon.
|
||||
|
||||
The beacons do not need to be strictly paired this way: rings or
|
||||
star-shaped networks are also possible. Each beacon has only a
|
||||
single destination, but can itself be the destination of several
|
||||
others.
|
||||
Right-clicking a second beacon with the key sets the destination of the second beacon to the position of the first beacon. To set up a return path, right-click the second beacon with the fragment, and then first beacon with the resulting key again.
|
||||
|
||||
Beacons will check that their destination is sane: the destination
|
||||
still needs to be a beacon, and the two nodes above it should be
|
||||
clear for walking / standing in. If your Minetest version supports
|
||||
it, the beacon will emerge the area prior to checking and teleporting.
|
||||
Emerging is merely a convenience, though.
|
||||
The beacons do not need to be strictly paired this way: rings or star-shaped networks are also possible. Each beacon has only a single destination, but can itself be the destination of several others.
|
||||
|
||||
Beacons have a maximum range of 20 nodes. If the destination is
|
||||
too far away, the beacon will turn red and will not function.
|
||||
To extend the range for a beacon, place "extenders" next to it,
|
||||
within a 7x7 horizontal square centered on the beacon.
|
||||
Once linked, you teleport between beacons by right-clicking them. Before teleporting, beacons will check that their destination is sane: the destination still needs to be a beacon, and the two nodes above it should be clear for walking/standing in.
|
||||
|
||||
Extenders come in three tiers: tier 1 extends all affected beacons
|
||||
by 5 nodes, tier 2 by 20 nodes, and tier 3 by 80 nodes. Placing
|
||||
or digging extenders will update affected beacons.
|
||||
Beacons have a default range of 20 nodes. If the destination is too far away, the beacon will turn red and will not function. To extend the range for a beacon, place "extenders" next to it, within a 7x7 horizontal square centered on the beacon, and at the same level or one node below the beacon.
|
||||
|
||||
Tier 1 extenders are crafted by placing an obsidian block, a wooden
|
||||
door, and another obsidian block in a horizontal row. Tier 2 extenders
|
||||
are crafted with an obsidian block in the middle, surrounded by a cross
|
||||
of four tier 1 extenders. Tier 3 extenders are crafted with an obsidian
|
||||
block surrounded by four tier 2 extenders.
|
||||
Extenders come in three tiers, with each being more powerful than the tier below. By default, tier 1, tier 2, and tier 3 extenders increase the range of nearby beacons by 5, 20, and 80 nodes, respectively.
|
||||
|
||||
Extenders can be colored with any of the dyes found in the dye mod.
|
||||
Colored extenders work just like regular extenders, both for
|
||||
teleporting and for recipes. To "uncolor" an extender, dye it grey.
|
||||
Extenders can be colored with any of the dyes found in the `dye` mod. Colored extenders work just like regular extenders, both for teleporting and for recipes.
|
||||
|
||||
Settings
|
||||
------------
|
||||
## Digilines
|
||||
|
||||
* `telemosaic_right_click_teleport` allow teleport on rightclick
|
||||
When the `digilines` mod is installed, telemosaic beacons can be controlled by sending digiline messages. By default, all beacons use the channel "telemosaic", but you can change the channel name via digilines.
|
||||
|
||||
Future plans
|
||||
------------
|
||||
For full documentaion, see [digilines.md](digilines.md).
|
||||
|
||||
* Particle and sound effects
|
||||
* Protected beacons (will not teleport if protected)
|
||||
## Dependencies
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
* default
|
||||
* doors
|
||||
* dye (optional)
|
||||
* `default`
|
||||
* `doors`
|
||||
* `dye` (optional)
|
||||
* `digilines` (optional)
|
||||
|
||||
Installation
|
||||
------------
|
||||
## License
|
||||
|
||||
Unzip the archive, rename the folder to to `bewarethedark` and
|
||||
place it in minetest/mods/
|
||||
|
||||
( Linux: If you have a linux system-wide installation place
|
||||
it in ~/.minetest/mods/. )
|
||||
|
||||
( If you only want this to be used in a single world, place
|
||||
the folder in worldmods/ in your worlddirectory. )
|
||||
|
||||
For further information or help see:
|
||||
http://wiki.minetest.com/wiki/Installing_Mods
|
||||
* Code: LGPL 2.1 (see included LICENSE file)
|
||||
* Textures: CC-BY-SA (see http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
* textures/telemosaic_beacon_protected_overlay.png MIT (https://notabug.org/TenPlus1/protector)
|
||||
|
26
abm.lua
@ -1,30 +1,22 @@
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Telemosaic beacon effect",
|
||||
nodenames = {"telemosaic:beacon"},
|
||||
interval = 2.0,
|
||||
chance = 2,
|
||||
catch_up = false,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
minetest.add_particlespawner({
|
||||
label = "Telemosaic beacon effect",
|
||||
nodenames = {"group:telemosaic_active"},
|
||||
interval = 2.0,
|
||||
chance = 2,
|
||||
catch_up = false,
|
||||
action = function(pos)
|
||||
minetest.add_particlespawner({
|
||||
amount = 4,
|
||||
time = 2,
|
||||
minpos = vector.add(pos, {x=-0.2, y=0, z=-0.2}),
|
||||
maxpos = vector.add(pos, {x=0.2, y=0, z=0.2}),
|
||||
minvel = {x=0, y=1, z=0},
|
||||
maxvel = {x=0, y=2, z=0},
|
||||
minacc = {x=0, y=0, z=0},
|
||||
maxacc = {x=0, y=0, z=0},
|
||||
minexptime = 1,
|
||||
maxexptime = 2,
|
||||
minsize = 1,
|
||||
maxsize = 1.7,
|
||||
collisiondetection = false,
|
||||
collision_removal = false,
|
||||
object_collision = false,
|
||||
vertical = false,
|
||||
texture = "telemosaic_particle_arrival.png",
|
||||
glow = 9
|
||||
glow = 9
|
||||
})
|
||||
end
|
||||
end
|
||||
})
|
||||
|
78
beacon.lua
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
for _,protected in pairs({true, false}) do
|
||||
|
||||
local node_name_suffix = protected and "_protected" or ""
|
||||
local texture_overlay = protected and "^telemosaic_beacon_protected_overlay.png" or ""
|
||||
local description_prefix = protected and "Protected " or ""
|
||||
|
||||
minetest.register_node("telemosaic:beacon_off"..node_name_suffix, {
|
||||
description = description_prefix.."Telemosaic Beacon",
|
||||
tiles = {
|
||||
"telemosaic_beacon_off.png"..texture_overlay,
|
||||
"telemosaic_beacon_side.png",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {cracky = 2, telemosaic = 1, telemosaic_off = 1},
|
||||
on_rightclick = telemosaic.rightclick,
|
||||
digilines = telemosaic.digilines,
|
||||
})
|
||||
|
||||
minetest.register_node("telemosaic:beacon"..node_name_suffix, {
|
||||
description = description_prefix.."Telemosaic Beacon Active (you hacker you!)",
|
||||
tiles = {
|
||||
"telemosaic_beacon_top.png"..texture_overlay,
|
||||
"telemosaic_beacon_side.png",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {cracky = 2, not_in_creative_inventory = 1, telemosaic = 1, telemosaic_active = 1},
|
||||
drop = "telemosaic:beacon_off"..node_name_suffix,
|
||||
on_rightclick = telemosaic.rightclick,
|
||||
digilines = telemosaic.digilines,
|
||||
})
|
||||
|
||||
minetest.register_node("telemosaic:beacon_err"..node_name_suffix, {
|
||||
description = description_prefix.."Telemosaic Beacon Error (you hacker you!)",
|
||||
tiles = {
|
||||
"telemosaic_beacon_err.png"..texture_overlay,
|
||||
"telemosaic_beacon_side.png",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {cracky = 2, not_in_creative_inventory = 1, telemosaic = 1, telemosaic_error = 1},
|
||||
drop = "telemosaic:beacon_off"..node_name_suffix,
|
||||
on_rightclick = telemosaic.rightclick,
|
||||
digilines = telemosaic.digilines,
|
||||
})
|
||||
|
||||
minetest.register_node("telemosaic:beacon_disabled"..node_name_suffix, {
|
||||
description = description_prefix.."Telemosaic Beacon Disabled (you hacker you!)",
|
||||
tiles = {
|
||||
"telemosaic_beacon_disabled.png"..texture_overlay,
|
||||
"telemosaic_beacon_side.png",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {cracky = 2, not_in_creative_inventory = 1, telemosaic = 1, telemosaic_disabled = 1},
|
||||
drop = "telemosaic:beacon_off"..node_name_suffix,
|
||||
on_rightclick = telemosaic.rightclick,
|
||||
digilines = telemosaic.digilines,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:beacon_off",
|
||||
recipe = {
|
||||
{"default:diamond", "doors:door_wood", "default:diamond"},
|
||||
{"default:obsidian","default:obsidian","default:obsidian"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:beacon_off_protected",
|
||||
type = "shapeless",
|
||||
recipe = {"telemosaic:beacon_off", "default:steel_ingot"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:beacon_off",
|
||||
type = "shapeless",
|
||||
recipe = {"telemosaic:beacon_off_protected"}
|
||||
})
|
46
crafts.lua
@ -1,46 +0,0 @@
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'telemosaic:beacon_off',
|
||||
recipe = {
|
||||
{'default:diamond', 'doors:door_wood', 'default:diamond'},
|
||||
{'default:obsidian','default:obsidian','default:obsidian'}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'telemosaic:beacon_off_protected',
|
||||
type = 'shapeless',
|
||||
recipe = {"telemosaic:beacon_off", "default:steel_ingot"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'telemosaic:extender_one',
|
||||
recipe = {
|
||||
{'default:obsidian','doors:door_wood','default:obsidian'}
|
||||
}
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'telemosaic:extender_two',
|
||||
recipe = {
|
||||
{'', 'group:telemosaic_extender_one',''},
|
||||
{'group:telemosaic_extender_one','default:obsidian','group:telemosaic_extender_one'},
|
||||
{'', 'group:telemosaic_extender_one',''}
|
||||
}
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'telemosaic:extender_three',
|
||||
recipe = {
|
||||
{'', 'group:telemosaic_extender_two',''},
|
||||
{'group:telemosaic_extender_two','default:obsidian','group:telemosaic_extender_two'},
|
||||
{'', 'group:telemosaic_extender_two',''}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
-- how to recycle a key
|
||||
minetest.register_craft({
|
||||
type = 'shapeless',
|
||||
recipe = {'telemosaic:key'},
|
||||
output = 'default:mese_crystal_fragment'
|
||||
})
|
@ -1,4 +0,0 @@
|
||||
default
|
||||
doors
|
||||
dye?
|
||||
digilines?
|
80
digilines.lua
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
local function digiline_receive(pos, _, channel, msg)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
local set_channel = meta:get("channel") or telemosaic.default_channel
|
||||
if channel ~= set_channel then
|
||||
return
|
||||
end
|
||||
|
||||
if type(msg) == "string" then
|
||||
-- Convert string command to table
|
||||
if msg == "get" or msg == "GET" then
|
||||
msg = {command = "get"}
|
||||
|
||||
elseif msg == "enable" or msg == "disable" then
|
||||
msg = {command = msg}
|
||||
|
||||
elseif msg:match("^setchannel .+") then
|
||||
local c = string.split(msg, " ", true, 1)
|
||||
msg = {command = c[1], channel = c[2]}
|
||||
|
||||
elseif msg:match("^setdest .+") then
|
||||
local c = string.split(msg, " ", true, 1)
|
||||
local p = string.split(c[2], ",")
|
||||
msg = {command = c[1], x = p[1], y = p[2], z = p[3]}
|
||||
end
|
||||
end
|
||||
|
||||
if type(msg) ~= "table" or not msg.command then
|
||||
return
|
||||
end
|
||||
|
||||
if msg.command == "get" then
|
||||
local dest = telemosaic.get_destination(pos)
|
||||
digilines.receptor_send(pos, digilines.rules.default, set_channel, {
|
||||
state = telemosaic.get_state(pos),
|
||||
origin = pos,
|
||||
target = dest,
|
||||
pos = pos,
|
||||
destination = dest,
|
||||
})
|
||||
|
||||
elseif msg.command == "setchannel" then
|
||||
if type(msg.channel) == "string" then
|
||||
meta:set_string("channel", msg.channel)
|
||||
end
|
||||
|
||||
elseif msg.command == "enable" then
|
||||
if telemosaic.get_state(pos) == "disabled" then
|
||||
telemosaic.set_state(pos, "active")
|
||||
end
|
||||
|
||||
elseif msg.command == "disable" then
|
||||
if telemosaic.get_state(pos) == "active" then
|
||||
telemosaic.set_state(pos, "disabled")
|
||||
end
|
||||
|
||||
elseif msg.command == "setdest" then
|
||||
local dest = msg.pos or {
|
||||
x = msg.x,
|
||||
y = msg.y,
|
||||
z = msg.z
|
||||
}
|
||||
if type(dest) ~= "table" then
|
||||
return
|
||||
end
|
||||
dest.x = tonumber(dest.x)
|
||||
dest.y = tonumber(dest.y)
|
||||
dest.z = tonumber(dest.z)
|
||||
if dest.x and dest.y and dest.z then
|
||||
telemosaic.set_destination(pos, dest)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
telemosaic.digilines = {
|
||||
effector = {
|
||||
action = digiline_receive
|
||||
}
|
||||
}
|
75
digilines.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Telemosaic digilines documentation
|
||||
|
||||
An overview of all commands and functionality, with example Lua code.
|
||||
|
||||
For convenience, and for using digiline buttons, all commands can also be sent as text.
|
||||
|
||||
### Change the digiline channel
|
||||
|
||||
**Lua:**
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "setchannel", channel = "whatever"})
|
||||
```
|
||||
|
||||
**Text:** `setchannel whatever`
|
||||
|
||||
### Disable a beacon
|
||||
|
||||
**Lua:**
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "disable"})
|
||||
```
|
||||
|
||||
**Text:** `disable`
|
||||
|
||||
### Enable a beacon
|
||||
|
||||
**Lua:**
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "enable"})
|
||||
```
|
||||
|
||||
**Text:** `enable`
|
||||
|
||||
### Set a new destination
|
||||
|
||||
**Lua:**
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "setdest", x = 0, y = 0, z = 0})
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "setdest", pos = {x = 0, y = 0, z = 0}})
|
||||
```
|
||||
|
||||
**Text:** `setdest 0,0,0`
|
||||
|
||||
Note that the destination will only be set if it's valid (beacon at destination).
|
||||
|
||||
### Get data from a beacon
|
||||
|
||||
**Lua**
|
||||
|
||||
```lua
|
||||
digiline_send("telemosaic", {command = "get"})
|
||||
```
|
||||
|
||||
**Text:** `get` or `GET`
|
||||
|
||||
Returns a table containing the following:
|
||||
|
||||
```lua
|
||||
{
|
||||
state = "active", -- or "disabled", "off", or "error"
|
||||
pos = {x = 1, y = 2, z = 3},
|
||||
destination = {x = 4, y = 5, z = 6},
|
||||
origin = {x = 1, y = 2, z = 3}, -- same as 'pos'
|
||||
target = {x = 4, y = 5, z = 6}, -- same as 'destination'
|
||||
}
|
||||
```
|
84
extender.lua
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
local has_dye = minetest.get_modpath("dye")
|
||||
|
||||
local function pretty_str(s)
|
||||
s = string.upper(string.sub(s, 1, 1))..string.sub(s, 2)
|
||||
local i = string.find(s, "_")
|
||||
if i then
|
||||
local c = string.upper(string.sub(s, i + 1, i + 1))
|
||||
s = string.gsub(s, "_.", " "..c)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local tiers = {"one", "two", "three"}
|
||||
|
||||
for i, range in pairs(telemosaic.extender_ranges) do
|
||||
local tier = tiers[i]
|
||||
|
||||
minetest.register_node("telemosaic:extender_"..tier, {
|
||||
description = "Telemosaic Extender, Tier "..i..(has_dye and " (Grey)" or ""),
|
||||
tiles = {
|
||||
"telemosaic_extender_"..tier..".png"
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {cracky = 2, telemosaic_extender = range, ["telemosaic_extender_"..tier] = 1},
|
||||
after_place_node = telemosaic.extender_place,
|
||||
after_dig_node = telemosaic.extender_dig,
|
||||
})
|
||||
|
||||
if has_dye then
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "telemosaic:extender_"..tier,
|
||||
recipe = {"group:telemosaic_extender_"..tier, "dye:grey"},
|
||||
})
|
||||
|
||||
for name, color in pairs(telemosaic.extender_colors) do
|
||||
minetest.register_node("telemosaic:extender_"..tier.."_"..name, {
|
||||
description = "Telemosaic Extender, Tier "..i.." ("..pretty_str(name)..")",
|
||||
tiles = {
|
||||
"telemosaic_extender_"..tier..".png^[colorize:"..color
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {
|
||||
cracky = 2, not_in_creative_inventory = 1,
|
||||
telemosaic_extender = range, ["telemosaic_extender_"..tier] = 1
|
||||
},
|
||||
after_place_node = telemosaic.extender_place,
|
||||
after_dig_node = telemosaic.extender_dig,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "telemosaic:extender_"..tier.."_"..name,
|
||||
recipe = {"group:telemosaic_extender_"..tier, "dye:"..name},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:extender_one",
|
||||
recipe = {
|
||||
{"default:obsidian", "doors:door_wood", "default:obsidian"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:extender_two",
|
||||
recipe = {
|
||||
{"", "group:telemosaic_extender_one", ""},
|
||||
{"group:telemosaic_extender_one", "default:obsidian", "group:telemosaic_extender_one"},
|
||||
{"", "group:telemosaic_extender_one", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "telemosaic:extender_three",
|
||||
recipe = {
|
||||
{"", "group:telemosaic_extender_two", ""},
|
||||
{"group:telemosaic_extender_two", "default:obsidian", "group:telemosaic_extender_two"},
|
||||
{"", "group:telemosaic_extender_two", ""}
|
||||
}
|
||||
})
|
367
functions.lua
Normal file
@ -0,0 +1,367 @@
|
||||
|
||||
-- Keeps a track of players to prevent teleport spamming
|
||||
local recent_teleports = {}
|
||||
|
||||
|
||||
-- Not the same as `minetest.hash_node_position` and `minetest.get_position_from_hash`
|
||||
local function hash_pos(pos)
|
||||
return math.floor(pos.x + 0.5)..':'..
|
||||
math.floor(pos.y + 0.5)..':'..
|
||||
math.floor(pos.z + 0.5)
|
||||
end
|
||||
|
||||
local function unhash_pos(hash)
|
||||
local list = string.split(hash, ':')
|
||||
local p = {
|
||||
x = tonumber(list[1]),
|
||||
y = tonumber(list[2]),
|
||||
z = tonumber(list[3])
|
||||
}
|
||||
if p.x and p.y and p.z then
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Wrap this function to incorporate travel checks from another mod
|
||||
function telemosaic.travel_allowed(player, src, dest)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function telemosaic.is_protected_beacon(pos, player_name)
|
||||
if minetest.get_node(pos).name == "telemosaic:beacon_protected" then
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function telemosaic.get_state(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
|
||||
if not def or not def.groups then return "invalid" end
|
||||
|
||||
if def.groups.telemosaic_off then return "off" end
|
||||
if def.groups.telemosaic_active then return "active" end
|
||||
if def.groups.telemosaic_disabled then return "disabled" end
|
||||
if def.groups.telemosaic_error then return "error" end
|
||||
|
||||
return "invalid"
|
||||
end
|
||||
|
||||
function telemosaic.set_state(pos, state)
|
||||
local node = minetest.get_node(pos)
|
||||
|
||||
if not string.match(node.name, "^telemosaic:beacon") then
|
||||
return -- Not a telemosaic!
|
||||
end
|
||||
|
||||
local new_name = "telemosaic:beacon"
|
||||
|
||||
if state == "off" then
|
||||
new_name = new_name.."_off"
|
||||
elseif state == "error" then
|
||||
new_name = new_name.."_err"
|
||||
elseif state == "disabled" then
|
||||
new_name = new_name.."_disabled"
|
||||
elseif state ~= "active" then
|
||||
return -- Can't set a state that doesn't exist
|
||||
end
|
||||
|
||||
if string.match(node.name, "protected$") then
|
||||
new_name = new_name.."_protected"
|
||||
end
|
||||
|
||||
-- Swap node instead of set node to keep the metadata
|
||||
minetest.swap_node(pos, {name = new_name})
|
||||
end
|
||||
|
||||
function telemosaic.is_valid_destination(pos)
|
||||
local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
local pos_top = {x = pos.x, y = pos.y + 2, z = pos.z}
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
local node_above = minetest.get_node_or_nil(pos_above)
|
||||
local node_top = minetest.get_node_or_nil(pos_top)
|
||||
|
||||
if not node or not node_above or not node_top then
|
||||
-- Need to load the map
|
||||
minetest.get_voxel_manip():read_from_map(pos, pos_top)
|
||||
node = node or minetest.get_node(pos)
|
||||
node_above = node_above or minetest.get_node(pos_above)
|
||||
node_top = node_top or minetest.get_node(pos_top)
|
||||
end
|
||||
|
||||
local valid = true
|
||||
|
||||
-- Check if there is a telemosaic at the destination
|
||||
if not string.match(node.name, "^telemosaic:beacon") then
|
||||
valid = false
|
||||
end
|
||||
|
||||
-- Check if the nodes above are not walkable (yes, confusing naming)
|
||||
if node_above.name ~= "air" and node_above.name ~= "vacuum:vacuum" then
|
||||
local def = minetest.registered_nodes[node_above.name]
|
||||
if def and def.walkable then
|
||||
return valid, false
|
||||
end
|
||||
end
|
||||
if node_top.name ~= "air" and node_top.name ~= "vacuum:vacuum" then
|
||||
local def = minetest.registered_nodes[node_top.name]
|
||||
if def and def.walkable then
|
||||
return valid, false
|
||||
end
|
||||
end
|
||||
|
||||
return valid, true
|
||||
end
|
||||
|
||||
function telemosaic.check_beacon(pos, player_name, all_checks)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
local dest = unhash_pos(meta:get_string("telemosaic:dest"))
|
||||
local state = telemosaic.get_state(pos)
|
||||
if not dest or state == "invalid" then
|
||||
return false
|
||||
end
|
||||
|
||||
local dist = vector.distance(pos, dest)
|
||||
local range = telemosaic.beacon_range
|
||||
|
||||
local pos1 = {x = pos.x - 3, y = pos.y - 1, z = pos.z - 3}
|
||||
local pos2 = {x = pos.x + 3, y = pos.y, z = pos.z + 3}
|
||||
local _, nodes = minetest.find_nodes_in_area(pos1, pos2, "group:telemosaic_extender")
|
||||
|
||||
for node_name, num in pairs(nodes) do
|
||||
range = range + (minetest.get_item_group(node_name, "telemosaic_extender") * num)
|
||||
end
|
||||
|
||||
if dist > range then
|
||||
-- Destination out of range, set beacon to error state
|
||||
telemosaic.set_state(pos, "error")
|
||||
if player_name then
|
||||
local needed = math.ceil(dist - range)
|
||||
minetest.chat_send_player(player_name,
|
||||
"You need to add extenders for "..needed.." nodes."
|
||||
)
|
||||
end
|
||||
return false
|
||||
elseif state == "error" or state == "off" then
|
||||
-- Not out of range anymore, set to active
|
||||
telemosaic.set_state(pos, "active")
|
||||
end
|
||||
|
||||
if not all_checks then
|
||||
return true -- Skip the destination check
|
||||
end
|
||||
|
||||
local valid, open = telemosaic.is_valid_destination(dest)
|
||||
if not valid then
|
||||
if player_name then
|
||||
minetest.chat_send_player(player_name,
|
||||
"No telemosaic at destination."
|
||||
)
|
||||
end
|
||||
return false
|
||||
elseif not open then
|
||||
if player_name then
|
||||
minetest.chat_send_player(player_name,
|
||||
"Destination is blocked."
|
||||
)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function telemosaic.get_destination(pos)
|
||||
local dest_hash = minetest.get_meta(pos):get_string("telemosaic:dest")
|
||||
return unhash_pos(dest_hash)
|
||||
end
|
||||
|
||||
function telemosaic.set_destination(pos, dest)
|
||||
local dest_hash = hash_pos(dest)
|
||||
local src_hash = hash_pos(pos)
|
||||
if src_hash == dest_hash or not telemosaic.is_valid_destination(dest) then
|
||||
return -- Don't allow setting invalid destination
|
||||
end
|
||||
minetest.get_meta(pos):set_string("telemosaic:dest", dest_hash)
|
||||
telemosaic.check_beacon(pos)
|
||||
end
|
||||
|
||||
function telemosaic.teleport(player, src, dest)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
-- Prevent teleport spamming
|
||||
recent_teleports[player_name] = true
|
||||
minetest.after(telemosaic.teleport_delay,
|
||||
function(name)
|
||||
recent_teleports[name] = nil
|
||||
end,
|
||||
player_name
|
||||
)
|
||||
|
||||
if telemosaic.digilines then
|
||||
-- Send a digiline message about the teleport
|
||||
local channel = minetest.get_meta(src):get("channel") or telemosaic.default_channel
|
||||
digilines.receptor_send(src, digilines.rules.default, channel, {
|
||||
player = player_name,
|
||||
origin = src,
|
||||
target = dest,
|
||||
pos = src,
|
||||
destination = dest,
|
||||
})
|
||||
end
|
||||
|
||||
dest.y = dest.y + 0.5 -- Teleport the player to above the telemosaic
|
||||
player:set_pos(dest)
|
||||
|
||||
minetest.sound_play({name = "telemosaic_departure", gain = 1}, {pos = src, max_hear_distance = 30})
|
||||
minetest.sound_play({name = "telemosaic_arrival", gain = 1}, {pos = dest, max_hear_distance = 30})
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 100,
|
||||
time = 0.25,
|
||||
minpos = {x = src.x, y = src.y + 0.3, z = src.z},
|
||||
maxpos = {x = src.x, y = src.y + 2, z = src.z},
|
||||
minvel = {x = 1, y = -6, z = 1},
|
||||
maxvel = {x = -1, y = -1, z = -1},
|
||||
minacc = {x = 0, y = -2, z = 0},
|
||||
maxacc = {x = 0, y = -6, z = 0},
|
||||
minexptime = 0.1,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.5,
|
||||
texture = "telemosaic_particle_departure.png",
|
||||
glow = 15,
|
||||
})
|
||||
minetest.add_particlespawner({
|
||||
amount = 100,
|
||||
time = 0.25,
|
||||
minpos = {x = dest.x, y = dest.y + 0.3, z = dest.z},
|
||||
maxpos = {x = dest.x, y = dest.y + 2, z = dest.z},
|
||||
minvel = {x = -1, y = 1, z = -1},
|
||||
maxvel = {x = 1, y = 6, z = 1},
|
||||
minacc = {x = 0, y = -2, z = 0},
|
||||
maxacc = {x = 0, y = -6, z = 0},
|
||||
minexptime = 0.1,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.5,
|
||||
texture = "telemosaic_particle_arrival.png",
|
||||
glow = 15,
|
||||
})
|
||||
end
|
||||
|
||||
function telemosaic.rightclick(pos, node, player, itemstack, pointed_thing)
|
||||
if player.is_fake_player or not minetest.is_player(player) then
|
||||
return itemstack -- No fake players!
|
||||
end
|
||||
|
||||
local item = itemstack:get_name()
|
||||
local player_name = player:get_player_name()
|
||||
local state = telemosaic.get_state(pos)
|
||||
|
||||
if item == "default:mese_crystal_fragment" then
|
||||
-- Try to create a telemosaic key
|
||||
if itemstack:get_count() ~= 1 then
|
||||
minetest.chat_send_player(player_name,
|
||||
"You can only use a singular mese crystal fragment to create a telemosaic key."
|
||||
)
|
||||
else
|
||||
return ItemStack({name = "telemosaic:key", metadata = hash_pos(pos)})
|
||||
end
|
||||
|
||||
elseif item == "telemosaic:key" then
|
||||
-- Try to set a new destination
|
||||
local dest_hash = itemstack:get_metadata()
|
||||
local src_hash = hash_pos(pos)
|
||||
if dest_hash ~= src_hash and not minetest.is_protected(pos, player_name) then
|
||||
local dest = unhash_pos(dest_hash)
|
||||
if not dest then
|
||||
-- This should never happen, but tell the player if it does
|
||||
minetest.chat_send_player(player_name,
|
||||
"Telemosaic key is invalid."
|
||||
)
|
||||
elseif not telemosaic.is_valid_destination(dest) then
|
||||
-- No point setting a destination that doesn't exist
|
||||
minetest.chat_send_player(player_name,
|
||||
"No telemosaic at new destination."
|
||||
)
|
||||
else
|
||||
-- Everything is good, set the destination and update the telemosaic
|
||||
minetest.get_meta(pos):set_string("telemosaic:dest", dest_hash)
|
||||
telemosaic.check_beacon(pos, player_name)
|
||||
end
|
||||
if player:get_player_control().sneak then
|
||||
return itemstack -- Don't destroy key
|
||||
end
|
||||
return ItemStack("default:mese_crystal_fragment")
|
||||
end
|
||||
|
||||
elseif state == "off" or player:get_player_control().sneak then
|
||||
-- Allow player to build on telemosaic
|
||||
local def = minetest.registered_nodes[item]
|
||||
if def and def.on_place and not vector.equals(pos, pointed_thing.above) then
|
||||
-- Need to create a fake pointed_thing to prevent recursion
|
||||
local new_pointed_thing = {
|
||||
type = "node",
|
||||
under = vector.new(pointed_thing.above),
|
||||
above = vector.new(pointed_thing.above)
|
||||
}
|
||||
return def.on_place(itemstack, player, new_pointed_thing)
|
||||
end
|
||||
|
||||
elseif state == "active" and not telemosaic.is_protected_beacon(pos, player_name) then
|
||||
if recent_teleports[player_name] then
|
||||
return itemstack -- Prevent teleport spamming, fail silently
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local dest = unhash_pos(meta:get_string("telemosaic:dest"))
|
||||
if telemosaic.check_beacon(pos, player_name, true) then
|
||||
if telemosaic.travel_allowed(player, pos, dest) then
|
||||
-- Teleport the player!
|
||||
telemosaic.teleport(player, pos, dest)
|
||||
else
|
||||
minetest.chat_send_player(player_name,
|
||||
"Travel to destination is not allowed."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == "disabled" then
|
||||
-- Tell the player why they can't use this telemosaic
|
||||
minetest.chat_send_player(player_name,
|
||||
"Telemosaic is disabled."
|
||||
)
|
||||
|
||||
elseif state == "error" then
|
||||
-- Check why the beacon is in error state, and tell the player
|
||||
telemosaic.check_beacon(pos, player_name)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function telemosaic.extender_place(pos, player, itemstack, pointed_thing)
|
||||
local pos1 = {x = pos.x - 3, y = pos.y, z = pos.z - 3}
|
||||
local pos2 = {x = pos.x + 3, y = pos.y + 1, z = pos.z + 3}
|
||||
local nodes = minetest.find_nodes_in_area(pos1, pos2, "group:telemosaic_error")
|
||||
|
||||
for _, node_pos in pairs(nodes) do
|
||||
-- Update telemosaic, and tell them if they need more extenders
|
||||
telemosaic.check_beacon(node_pos, player:get_player_name())
|
||||
end
|
||||
end
|
||||
|
||||
function telemosaic.extender_dig(pos, oldnode, oldmetadata, player)
|
||||
local pos1 = {x = pos.x - 3, y = pos.y, z = pos.z - 3}
|
||||
local pos2 = {x = pos.x + 3, y = pos.y + 1, z = pos.z + 3}
|
||||
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"group:telemosaic_active", "group:telemosaic_disabled"})
|
||||
|
||||
for _, node_pos in pairs(nodes) do
|
||||
-- Update the telemosaic, but don't tell the player anything, they are probably removing it anyway
|
||||
telemosaic.check_beacon(node_pos)
|
||||
end
|
||||
end
|
71
init.lua
@ -7,44 +7,43 @@ A mod which provides player-placed teleport pads
|
||||
|
||||
Copyright (C) 2015 Ben Deutsch <ben@bendeutsch.de>
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA
|
||||
|
||||
]]
|
||||
|
||||
telemosaic = {
|
||||
-- configuration
|
||||
config = {
|
||||
-- keep emerge_delay lower than teleport_delay!
|
||||
emerge_delay = 0.5, -- seconds
|
||||
teleport_delay = tonumber(minetest.settings:get("telemosaic_teleport_delay")) or 2.0, -- seconds
|
||||
beacon_range = tonumber(minetest.settings:get("telemosaic_beacon_range")) or 20.0, -- max teleport distance
|
||||
extender_ranges = {
|
||||
-- note: not adding beacons here, since they don't extend
|
||||
-- also: base name of colored versions
|
||||
['telemosaic:extender_one'] = tonumber(minetest.settings:get("telemosaic_extender_one_range")) or 5.0,
|
||||
['telemosaic:extender_two'] = tonumber(minetest.settings:get("telemosaic_extender_two_range")) or 20.0,
|
||||
['telemosaic:extender_three'] = tonumber(minetest.settings:get("telemosaic_extender_three_range")) or 80.0,
|
||||
},
|
||||
},
|
||||
teleport_delay = tonumber(minetest.settings:get("telemosaic_teleport_delay")) or 1.0,
|
||||
beacon_range = tonumber(minetest.settings:get("telemosaic_beacon_range")) or 20.0,
|
||||
default_channel = minetest.settings:get("telemosaic_default_channel") or "telemosaic",
|
||||
extender_ranges = {
|
||||
tonumber(minetest.settings:get("telemosaic_extender_one_range")) or 5.0,
|
||||
tonumber(minetest.settings:get("telemosaic_extender_two_range")) or 20.0,
|
||||
tonumber(minetest.settings:get("telemosaic_extender_three_range")) or 80.0,
|
||||
},
|
||||
extender_colors = {
|
||||
white = "#ffffffa0",
|
||||
dark_grey = "#00000090",
|
||||
black = "#000000e0",
|
||||
violet = "#400080b0",
|
||||
blue = "#0000ffb0",
|
||||
cyan = "#00ffffb0",
|
||||
dark_green = "#007000b0",
|
||||
green = "#00ff00b0",
|
||||
yellow = "#ffff00b0",
|
||||
brown = "#402000c0",
|
||||
orange = "#ff8000b0",
|
||||
red = "#ff0000b0",
|
||||
magenta = "#ff00ffb0",
|
||||
pink = "#ff8080b0",
|
||||
},
|
||||
}
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
dofile(modpath.."/teleport.lua")
|
||||
dofile(modpath.."/crafts.lua")
|
||||
dofile(modpath.."/abm.lua")
|
||||
local MP = minetest.get_modpath("telemosaic")
|
||||
|
||||
if minetest.get_modpath("digilines") then
|
||||
dofile(MP.."/digilines.lua")
|
||||
end
|
||||
|
||||
dofile(MP.."/functions.lua")
|
||||
dofile(MP.."/beacon.lua")
|
||||
dofile(MP.."/extender.lua")
|
||||
dofile(MP.."/key.lua")
|
||||
dofile(MP.."/abm.lua")
|
||||
|
27
key.lua
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
minetest.register_tool("telemosaic:key", {
|
||||
description = "Telemosaic Key",
|
||||
inventory_image = "telemosaic_key.png",
|
||||
stack_max = 1,
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
on_use = function(itemstack, player, pointed_thing)
|
||||
if pointed_thing.type == "node" then
|
||||
-- Use key to toggle telemosaic on/off
|
||||
local name = player:get_player_name()
|
||||
local pos = pointed_thing.under
|
||||
local state = telemosaic.get_state(pos)
|
||||
if state == "active" and not minetest.is_protected(pos, name) then
|
||||
telemosaic.set_state(pos, "disabled")
|
||||
elseif state == "disabled" and not minetest.is_protected(pos, name) then
|
||||
telemosaic.set_state(pos, "active")
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
recipe = {"telemosaic:key"},
|
||||
output = "default:mese_crystal_fragment"
|
||||
})
|
4
mod.conf
Normal file
@ -0,0 +1,4 @@
|
||||
name = telemosaic
|
||||
description = A Minetest mod for user-generated teleportation pads.
|
||||
depends = default, doors
|
||||
optional_depends = dye, digilines
|
BIN
screenshot.png
Normal file
After Width: | Height: | Size: 64 KiB |
@ -1,14 +1,17 @@
|
||||
#The range of the teleport beacon
|
||||
telemosaic_beacon_range (Beacon range) string 20.0
|
||||
#The base range of the teleport beacon
|
||||
telemosaic_beacon_range (Beacon range) int 20
|
||||
|
||||
#The range of the tier one extender
|
||||
telemosaic_extender_one_range (Extender tier one range) string 5.0
|
||||
#The extending range of the tier 1 extender
|
||||
telemosaic_extender_one_range (Extender tier 1 range) int 5
|
||||
|
||||
#The range of the tier two extender
|
||||
telemosaic_extender_two_range (Extender tier two range) string 20.0
|
||||
#The extending range of the tier 2 extender
|
||||
telemosaic_extender_two_range (Extender tier 2 range) int 20
|
||||
|
||||
#The range of the tier three extender
|
||||
telemosaic_extender_three_range (Extender tier three range) string 80.0
|
||||
#The extending range of the tier 3 extender
|
||||
telemosaic_extender_three_range (Extender tier 2 range) int 80
|
||||
|
||||
#The delay before player is teleported
|
||||
telemosaic_teleport_delay (Teleport delay) string 2.0
|
||||
#The minimum delay between player teleports
|
||||
telemosaic_teleport_delay (Teleport delay) float 1.0
|
||||
|
||||
# The default digilines channel for beacons
|
||||
telemosaic_default_channel (Default channel) string telemosaic
|
||||
|
541
teleport.lua
@ -1,541 +0,0 @@
|
||||
local C = telemosaic.config
|
||||
|
||||
local recent_teleports = {}
|
||||
|
||||
local function hash_pos(pos)
|
||||
return math.floor(pos.x + 0.5) .. ':' ..
|
||||
math.floor(pos.y + 0.5) .. ':' ..
|
||||
math.floor(pos.z + 0.5)
|
||||
end
|
||||
local function unhash_pos(hash)
|
||||
local pos = {}
|
||||
local list = string.split(hash, ':')
|
||||
pos.x = tonumber(list[1])
|
||||
pos.y = tonumber(list[2])
|
||||
pos.z = tonumber(list[3])
|
||||
return pos
|
||||
end
|
||||
|
||||
local function count_extenders(pos)
|
||||
local extended = 0.0
|
||||
for z=-3,3 do
|
||||
for x=-3,3 do
|
||||
local node = minetest.get_node({ x=pos.x+x, y=pos.y, z=pos.z+z})
|
||||
local name = node.name
|
||||
-- trim color off the back
|
||||
name = string.gsub(name, '^(telemosaic:extender_%a+)_.+', '%1')
|
||||
extended = extended + ( C.extender_ranges[name] or 0.0 )
|
||||
end
|
||||
end
|
||||
--print("Total extended: " .. extended)
|
||||
return extended
|
||||
end
|
||||
|
||||
-- returns true if the beacon is in an error state (protected or not)
|
||||
local function is_err_beacon(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
if not node then
|
||||
-- no node or name
|
||||
return false
|
||||
end
|
||||
return node.name == "telemosaic:beacon_err" or node.name == "telemosaic:beacon_err_protected"
|
||||
end
|
||||
|
||||
-- returns true if the beacon is disabled state (protected or not)
|
||||
local function is_disabled_beacon(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
if not node then
|
||||
-- no node or name
|
||||
return false
|
||||
end
|
||||
return node.name == "telemosaic:beacon_disabled" or node.name == "telemosaic:beacon_disabled_protected"
|
||||
end
|
||||
|
||||
-- swaps out the beacon with the new state suffix
|
||||
local function swap_beacon(pos, new_state_suffix)
|
||||
local node = minetest.get_node(pos)
|
||||
if not node then
|
||||
-- no node at pos
|
||||
return
|
||||
end
|
||||
|
||||
local name = node.name
|
||||
local new_node_name = "telemosaic:beacon" .. new_state_suffix
|
||||
|
||||
if string.match(name, "_protected") then
|
||||
-- protected beacon
|
||||
new_node_name = new_node_name .. "_protected"
|
||||
end
|
||||
|
||||
minetest.swap_node(pos, { name = new_node_name })
|
||||
end
|
||||
|
||||
|
||||
local function extender_place(placepos, placer, itemstack, pointed_thing)
|
||||
-- go over all possible *err* beacons, and update them
|
||||
for z=-3,3 do
|
||||
for x=-3,3 do
|
||||
local pos = { x=placepos.x+x, y=placepos.y, z=placepos.z+z }
|
||||
if is_err_beacon(pos) then
|
||||
-- candidate!
|
||||
local dest_hash = minetest.get_meta(pos):get_string('telemosaic:dest')
|
||||
|
||||
if dest_hash ~= nil and dest_hash ~= '' then
|
||||
local dest = unhash_pos(dest_hash)
|
||||
local extended = count_extenders(pos)
|
||||
local dist = vector.distance(pos, dest)
|
||||
|
||||
if dist <= C.beacon_range + extended then
|
||||
-- upgrade :-)
|
||||
swap_beacon(pos, "")
|
||||
|
||||
else
|
||||
local count = math.ceil(dist - (C.beacon_range + extended))
|
||||
minetest.chat_send_player(
|
||||
placer:get_player_name(),
|
||||
"You still need to add extensions for "..count.." nodes"
|
||||
)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function extender_dig(digpos, oldnode, oldmetadata, digger)
|
||||
-- go over all possible *actual* beacons, and update them
|
||||
for z=-3,3 do
|
||||
for x=-3,3 do
|
||||
local pos = { x=digpos.x+x, y=digpos.y, z=digpos.z+z }
|
||||
local node = minetest.get_node(pos)
|
||||
if node ~= nil and (node.name == 'telemosaic:beacon'
|
||||
or node.name == 'telemosaic:beacon_disabled') then
|
||||
-- candidate!
|
||||
local dest_hash = minetest.get_meta(pos):get_string('telemosaic:dest')
|
||||
if dest_hash ~= nil and dest_hash ~= '' then
|
||||
local dest = unhash_pos(dest_hash)
|
||||
local extended = count_extenders(pos)
|
||||
local dist = vector.distance(pos, dest)
|
||||
|
||||
if dist > C.beacon_range + extended then
|
||||
-- downgrade :-(
|
||||
swap_beacon(pos, "_err")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_teleport_dest(dest)
|
||||
-- check the destination node for beacons, and the two nodes
|
||||
-- above for "walkthrough"
|
||||
-- "ignore" is ok, we could not emerge in time then.
|
||||
local dest_bot = minetest.get_node({ x = dest.x, y = dest.y , z = dest.z })
|
||||
local dest_mid = minetest.get_node({ x = dest.x, y = dest.y+1, z = dest.z })
|
||||
local dest_top = minetest.get_node({ x = dest.x, y = dest.y+2, z = dest.z })
|
||||
--print ("Looking at " .. dest_bot.name .. ", " .. dest_mid.name .. ", " .. dest_top.name)
|
||||
local dest_ok = true
|
||||
if dest_bot.name ~= 'ignore' and not string.match(dest_bot.name, '^telemosaic:beacon') then
|
||||
dest_ok = false
|
||||
--print("Bottom is not beacon")
|
||||
end
|
||||
if dest_mid.name ~= 'ignore' and dest_mid.name ~= 'air' then
|
||||
local def = minetest.registered_nodes[dest_mid.name]
|
||||
if def and def.walkable then
|
||||
dest_ok = false
|
||||
--print("Mid is walkable")
|
||||
end
|
||||
end
|
||||
if dest_top.name ~= 'ignore' and dest_top.name ~= 'air' then
|
||||
local def = minetest.registered_nodes[dest_top.name]
|
||||
if def and def.walkable then
|
||||
dest_ok = false
|
||||
--print("Top is walkable")
|
||||
end
|
||||
end
|
||||
return dest_ok
|
||||
end
|
||||
|
||||
local function is_protected_beacon(pos, player)
|
||||
if minetest.get_node(pos).name == 'telemosaic:beacon_protected' then
|
||||
-- check protection on protected telemosaic
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
-- protected telemosaic
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- not a protected telemosaic
|
||||
return false
|
||||
end
|
||||
|
||||
-- digilines optional support
|
||||
local DEFAULT_CHANNEL = "telemosaic"
|
||||
|
||||
local on_digiline_receive = function(pos, _, channel, msg)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local setchan = meta:contains("channel") and meta:get_string("channel") or DEFAULT_CHANNEL
|
||||
if channel ~= setchan then
|
||||
return false
|
||||
end
|
||||
if type(msg) == "string" then
|
||||
local a = string.split(msg, " ")
|
||||
msg = { command = a[1], channel = a[2] }
|
||||
elseif type(msg) ~= "table" then
|
||||
return false
|
||||
end
|
||||
if msg.command == "setchannel" then
|
||||
if type(msg.channel) == "string" then
|
||||
meta:set_string("channel", msg.channel)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- for all states but disabled and active beacons: just accept wiring and channel setting
|
||||
local telemosaic_digiline_base = {
|
||||
receptor = {},
|
||||
effector = { action = on_digiline_receive }
|
||||
}
|
||||
-- for active and disabled beacon , also implement state switching
|
||||
local telemosaic_digiline_switching = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = function(pos, _, channel, msg)
|
||||
if on_digiline_receive(pos, _, channel, msg) then
|
||||
local cmd = (type(msg) == "table") and msg.command or msg
|
||||
if cmd == "enable" then
|
||||
swap_beacon(pos, "")
|
||||
elseif cmd == "disable" then
|
||||
swap_beacon(pos, "_disabled")
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
-- teleports the player with given telemosaic pos
|
||||
local function do_teleport(pos, player)
|
||||
|
||||
-- prevent teleport spamming
|
||||
local player_name = player:get_player_name()
|
||||
if recent_teleports[player_name] then
|
||||
return
|
||||
end
|
||||
|
||||
if is_err_beacon(pos) or is_disabled_beacon(pos) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
local dest_hash = meta:get_string('telemosaic:dest')
|
||||
if dest_hash ~= nil and dest_hash ~= '' then
|
||||
local dest = unhash_pos(dest_hash)
|
||||
|
||||
-- test destination nodes
|
||||
local dest_ok = check_teleport_dest(dest)
|
||||
|
||||
--print("Ping to " .. dest_hash)
|
||||
local extended = count_extenders(pos)
|
||||
-- check for range before teleport (with leeway)
|
||||
local dist = vector.distance(pos, dest)
|
||||
--print("Dist :" .. (dist-0.5) .. " to " .. (C.beacon_range + extended))
|
||||
|
||||
if dest_ok and dist - 0.5 <= C.beacon_range + extended and telemosaic.travel_allowed(player, pos, dest) then
|
||||
|
||||
if telemosaic_digiline_switching ~= nil then
|
||||
local chan = meta:contains("channel") and meta:get_string("channel") or DEFAULT_CHANNEL
|
||||
digilines.receptor_send(pos, digilines.rules.default, chan, {
|
||||
player = player:get_player_name(),
|
||||
hashpos = { origin = hash_pos(pos), target = dest_hash },
|
||||
origin = pos,
|
||||
target = dest,
|
||||
})
|
||||
end
|
||||
|
||||
dest.y = dest.y + 0.5
|
||||
|
||||
minetest.sound_play( {name="telemosaic_set", gain=1}, {pos=pos, max_hear_distance=30})
|
||||
minetest.add_particlespawner({
|
||||
amount = 100,
|
||||
time = 0.25,
|
||||
minpos = {x=pos.x, y=pos.y+0.3, z=pos.z},
|
||||
maxpos = {x=pos.x, y=pos.y+2, z=pos.z},
|
||||
minvel = {x = 1, y = -6, z = 1},
|
||||
maxvel = {x = -1, y = -1, z = -1},
|
||||
minacc = {x = 0, y = -2, z = 0},
|
||||
maxacc = {x = 0, y = -6, z = 0},
|
||||
minexptime = 0.1,
|
||||
maxexptime = 1,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.5,
|
||||
texture = "telemosaic_particle_departure.png",
|
||||
glow = 15,
|
||||
})
|
||||
player:set_pos(dest)
|
||||
|
||||
minetest.sound_play( {name="telemosaic_teleport", gain=1}, {pos=dest, max_hear_distance=30})
|
||||
minetest.add_particlespawner({
|
||||
amount = 100,
|
||||
time = 0.25,
|
||||
minpos = {x=dest.x, y=dest.y+0.3, z=dest.z},
|
||||
maxpos = {x=dest.x, y=dest.y+2, z=dest.z},
|
||||
minvel = {x = -1, y = 1, z = -1},
|
||||
maxvel = {x = 1, y = 6, z = 1},
|
||||
minacc = {x = 0, y = -2, z = 0},
|
||||
maxacc = {x = 0, y = -6, z = 0},
|
||||
minexptime = 0.1,
|
||||
maxexptime = 1,
|
||||
minsize = 0.5,
|
||||
maxsize = 1.5,
|
||||
texture = "telemosaic_particle_arrival.png",
|
||||
glow = 15,
|
||||
})
|
||||
|
||||
-- prevent teleport spamming
|
||||
recent_teleports[player_name] = true
|
||||
minetest.after(1,
|
||||
function(name)
|
||||
recent_teleports[name] = nil
|
||||
end,
|
||||
player_name)
|
||||
-- else
|
||||
-- beacon is in error, one way or another.
|
||||
-- but don't swap it out - we won't get it back otherwise!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function beacon_rightclick(pos, node, player, itemstack, pointed_thing)
|
||||
local name = itemstack:get_name()
|
||||
--print("Clicked by a " ..name)
|
||||
if name == 'default:mese_crystal_fragment' and itemstack:get_count() == 1 then
|
||||
--print("Clicked by a single key")
|
||||
itemstack = ItemStack({
|
||||
name = "telemosaic:key",
|
||||
count = 1,
|
||||
wear = 0,
|
||||
metadata = hash_pos(pointed_thing.under),
|
||||
})
|
||||
elseif name == 'telemosaic:key' then
|
||||
local posstring = itemstack:get_metadata()
|
||||
local thispos = hash_pos(pointed_thing.under)
|
||||
--print("Key with metadata " .. posstring)
|
||||
if posstring ~= thispos and not minetest.is_protected(pointed_thing.under, player:get_player_name()) then
|
||||
local dest_pos = unhash_pos(posstring)
|
||||
local extended = count_extenders(pointed_thing.under)
|
||||
if vector.distance(dest_pos, pointed_thing.under) <= C.beacon_range + extended then
|
||||
swap_beacon(pointed_thing.under, "")
|
||||
else
|
||||
swap_beacon(pointed_thing.under, "_err")
|
||||
end
|
||||
|
||||
-- set the destination anyway, it just won't work as
|
||||
-- long as the beacon is in err state
|
||||
local meta = minetest.get_meta(pointed_thing.under)
|
||||
meta:set_string('telemosaic:dest', posstring)
|
||||
--print ("set to " .. meta:get_string('telemosaic:dest'))
|
||||
itemstack = ItemStack({
|
||||
name = "default:mese_crystal_fragment",
|
||||
count = 1, wear = 0,
|
||||
})
|
||||
end
|
||||
-- elseif player:get_player_control().sneak then
|
||||
-- NOTE: shift-place commented out due to recursion-issue
|
||||
-- NOTE: https://github.com/pandorabox-io/pandorabox.io/issues/439
|
||||
-- NOTE: workaround: place another node below/behind to build on the beacon
|
||||
-- normal place
|
||||
-- local def = minetest.registered_nodes[itemstack:get_name()]
|
||||
-- if def then
|
||||
-- return def.on_place(itemstack, player, pointed_thing)
|
||||
-- end
|
||||
|
||||
elseif not is_protected_beacon(pos, player) then
|
||||
-- teleport when right-clicked
|
||||
do_teleport(pos, player)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
|
||||
-- register protected and plain beacons with different suffixes, names and textures
|
||||
for _,is_protected in pairs({ true, false }) do
|
||||
|
||||
local node_name_suffix = ""
|
||||
local texture_overlay = ""
|
||||
local description_prefix = ""
|
||||
|
||||
if is_protected then
|
||||
node_name_suffix = "_protected"
|
||||
texture_overlay = "^telemosaic_beacon_protected_overlay.png"
|
||||
description_prefix = "Protected "
|
||||
end
|
||||
|
||||
minetest.register_node('telemosaic:beacon' .. node_name_suffix, {
|
||||
description = description_prefix .. 'Telemosaic beacon (on)',
|
||||
tiles = {
|
||||
'telemosaic_beacon_top.png' .. texture_overlay,
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = { cracky = 2, not_in_creative_inventory = 1 },
|
||||
drop = 'telemosaic:beacon_off',
|
||||
on_rightclick = beacon_rightclick,
|
||||
digiline = telemosaic_digiline_switching
|
||||
})
|
||||
|
||||
minetest.register_node('telemosaic:beacon_err' .. node_name_suffix, {
|
||||
description = description_prefix .. 'Telemosaic beacon (err)',
|
||||
tiles = {
|
||||
'telemosaic_beacon_err.png' .. texture_overlay,
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = { cracky = 2, not_in_creative_inventory = 1 },
|
||||
drop = 'telemosaic:beacon_off',
|
||||
on_rightclick = beacon_rightclick,
|
||||
digiline = telemosaic_digiline_base
|
||||
})
|
||||
|
||||
minetest.register_node('telemosaic:beacon_off' .. node_name_suffix, {
|
||||
description = description_prefix .. 'Telemosaic beacon',
|
||||
tiles = {
|
||||
'telemosaic_beacon_off.png' .. texture_overlay,
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = { cracky = 2 },
|
||||
on_rightclick = beacon_rightclick,
|
||||
on_construct = function(pos)
|
||||
minetest.get_meta(pos):set_string("channel", DEFAULT_CHANNEL)
|
||||
end,
|
||||
digiline = telemosaic_digiline_base
|
||||
})
|
||||
|
||||
minetest.register_node('telemosaic:beacon_disabled' .. node_name_suffix, {
|
||||
description = description_prefix .. 'Telemosaic beacon (disabled)',
|
||||
tiles = {
|
||||
'telemosaic_beacon_disabled.png' .. texture_overlay,
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
'telemosaic_beacon_side.png',
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = { cracky = 2, not_in_creative_inventory = 1 },
|
||||
drop = 'telemosaic:beacon_off',
|
||||
on_rightclick = beacon_rightclick,
|
||||
digiline = telemosaic_digiline_switching
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
|
||||
minetest.register_tool('telemosaic:key', {
|
||||
description = 'Telemosaic key',
|
||||
inventory_image = 'telemosaic_key.png',
|
||||
stack_max = 1,
|
||||
groups = { not_in_creative_inventory = 1 },
|
||||
})
|
||||
|
||||
-- extenders come in three strengths, and many colors
|
||||
|
||||
local strengths = {
|
||||
-- index starts at 1
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
}
|
||||
local colors = {
|
||||
-- TODO: localisation
|
||||
-- default: { name= 'grey', value= '#ffffff00'},
|
||||
{ name= 'white', value= '#ffffff80'},
|
||||
{ name= 'dark_grey', value= '#00000080'},
|
||||
{ name= 'black', value= '#000000c0'},
|
||||
{ name= 'violet', value= '#40008080'},
|
||||
{ name= 'blue', value= '#0000ff80'},
|
||||
{ name= 'cyan', value= '#00ffff80'},
|
||||
{ name= 'dark_green', value= '#00800080'},
|
||||
{ name= 'green', value= '#00ff0080'},
|
||||
{ name= 'yellow', value= '#ffff0080'},
|
||||
{ name= 'brown', value= '#80400080'},
|
||||
{ name= 'orange', value= '#ff800080'},
|
||||
{ name= 'red', value= '#ff000080'},
|
||||
{ name= 'magenta', value= '#ff00ff80'},
|
||||
{ name= 'pink', value= '#ff808080'},
|
||||
}
|
||||
|
||||
for num, strength in ipairs(strengths) do
|
||||
minetest.register_node(string.format('telemosaic:extender_%s', strength), {
|
||||
description = string.format('Telemosaic extender, tier %d', num),
|
||||
tiles = {
|
||||
string.format('telemosaic_extender_%s.png', strength)
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = { cracky = 2, [string.format('telemosaic_extender_%s', strength)] = 1 },
|
||||
after_place_node = extender_place,
|
||||
after_dig_node = extender_dig,
|
||||
})
|
||||
-- colored versions, not in creative inventory, if dyes available
|
||||
if minetest.get_modpath('dye') then
|
||||
for _,c in ipairs(colors) do
|
||||
minetest.register_node(string.format('telemosaic:extender_%s_%s', strength, c.name), {
|
||||
description = string.format('Telemosaic extender, tier %d (%s)', num, c.name),
|
||||
tiles = {
|
||||
string.format('telemosaic_extender_%s.png^[colorize:%s', strength, c.value),
|
||||
},
|
||||
paramtype = 'light',
|
||||
groups = {
|
||||
cracky = 2,
|
||||
[string.format('telemosaic_extender_%s', strength)] = 1,
|
||||
not_in_creative_inventory = 1
|
||||
},
|
||||
after_place_node = extender_place,
|
||||
after_dig_node = extender_dig,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- coloring recipes
|
||||
if minetest.get_modpath('dye') then
|
||||
for num, strength in ipairs(strengths) do
|
||||
-- uncolor
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = string.format('telemosaic:extender_%s', strength),
|
||||
recipe = { string.format('group:telemosaic_extender_%s', strength), 'dye:grey' },
|
||||
})
|
||||
-- color with dye
|
||||
for _,c in ipairs(colors) do
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = string.format('telemosaic:extender_%s_%s', strength, c.name),
|
||||
recipe = { string.format('group:telemosaic_extender_%s', strength), string.format('dye:%s', c.name) },
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
telemosaic.travel_allowed = function(player, src, dest)
|
||||
return true
|
||||
end
|
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 126 B |
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 125 B |
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 81 B |
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 126 B |
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 154 B |
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 147 B |
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 125 B After Width: | Height: | Size: 107 B |
Before Width: | Height: | Size: 106 B After Width: | Height: | Size: 92 B |