Skip to content

Commit a49e4dd

Browse files
authored
Improve digistuff:detector and digistuff:camera (#29)
1 parent 813a64b commit a49e4dd

File tree

6 files changed

+256
-99
lines changed

6 files changed

+256
-99
lines changed

camera.lua

Lines changed: 115 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
1+
2+
local formspec = "size[8,4]"..
3+
"field[1.3,1;6,1;channel;Channel;${channel}]"..
4+
"field[1.3,2;3,1.3;radius;Radius;${radius}]"..
5+
"field[4.3,2;3,1.3;distance;Distance;${distance}]"..
6+
"button_exit[4,3;3,1;submit;Save]"
7+
8+
local function get_formspec(enabled)
9+
if enabled then
10+
return formspec.."button[1,3;3,1;disable;Disable]"
11+
else
12+
return formspec.."button[1,3;3,1;enable;Enable]"
13+
end
14+
end
15+
16+
local function search_for_players(pos, send_empty)
17+
local meta = minetest.get_meta(pos)
18+
local distance = meta:get_int("distance")
19+
local dir = minetest.facedir_to_dir(minetest.get_node(pos).param2)
20+
local spot = vector.add(pos, vector.multiply(dir, -distance))
21+
local node = minetest.get_node(spot)
22+
while node.name == "air" and pos.y - spot.y < 10 do
23+
spot.y = spot.y - 1
24+
node = minetest.get_node(spot)
25+
end
26+
if node.name == "air" or node.name == "ignore" then
27+
return true
28+
end
29+
local radius = meta:get_int("radius")
30+
local found = {}
31+
for _,player in pairs(minetest.get_connected_players()) do
32+
if vector.distance(spot, player:get_pos()) <= radius then
33+
table.insert(found, player:get_player_name())
34+
end
35+
end
36+
if #found > 0 or send_empty == true then
37+
local channel = meta:get_string("channel")
38+
digilines.receptor_send(pos, digilines.rules.default, channel, found)
39+
end
40+
return true
41+
end
42+
143
minetest.register_node("digistuff:camera", {
44+
description = "Digilines Camera",
245
tiles = {
346
"digistuff_camera_top.png",
447
"digistuff_camera_bottom.png",
@@ -7,84 +50,100 @@ minetest.register_node("digistuff:camera", {
750
"digistuff_camera_back.png",
851
"digistuff_camera_front.png",
952
},
10-
digiline = {
11-
receptor = {}
12-
},
13-
_digistuff_channelcopier_fieldname = "channel",
14-
groups = {cracky=2},
1553
paramtype = "light",
1654
paramtype2 = "facedir",
1755
drawtype = "nodebox",
1856
node_box = {
1957
type = "fixed",
2058
fixed = {
21-
{-0.1,-0.5,-0.28,0.1,-0.3,0.3}, --Camera Body
22-
{-0.045,-0.42,-0.34,0.045,-0.36,-0.28}, -- Lens
23-
{-0.05,-0.9,-0.05,0.05,-0.5,0.05}, --Pole
24-
}
59+
{-0.1,-0.5,-0.28,0.1,-0.3,0.3}, -- Camera Body
60+
{-0.045,-0.42,-0.34,0.045,-0.36,-0.28}, -- Lens
61+
{-0.05,-0.9,-0.05,0.05,-0.5,0.05}, -- Pole
62+
}
2563
},
2664
selection_box = {
2765
type = "fixed",
2866
fixed = {
29-
{-0.1,-0.5,-0.34,0.1,-0.3,0.3}, --Camera Body
30-
}
67+
{-0.1,-0.5,-0.34,0.1,-0.3,0.3},
68+
}
3169
},
32-
description = "Digilines Camera",
70+
sounds = default and default.node_sound_stone_defaults(),
71+
groups = {cracky = 2},
3372
on_construct = function(pos)
3473
local meta = minetest.get_meta(pos)
35-
meta:set_string("formspec","size[8,6;]field[1,1;6,2;channel;Channel;${channel}]field[1,2;6,2;radius;Radius (max 10);${radius}]field[1,3;6,2;distance;Distance (max 20);${distance}]button_exit[2.25,4;3,1;submit;Save]")
74+
meta:set_string("formspec", get_formspec(true))
75+
meta:set_int("radius", 1)
76+
meta:set_int("distance", 0)
77+
minetest.get_node_timer(pos):start(1)
3678
end,
37-
on_receive_fields = function(pos, formname, fields, sender)
38-
local name = sender:get_player_name()
39-
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
40-
minetest.record_protection_violation(pos,name)
79+
on_receive_fields = function(pos, _, fields, player)
80+
if minetest.is_protected(pos, player:get_player_name()) then
4181
return
4282
end
4383
local meta = minetest.get_meta(pos)
44-
if fields.channel then meta:set_string("channel",fields.channel) end
45-
if fields.distance and tonumber(fields.distance) then meta:set_int("distance",math.max(math.min(20,fields.distance),0)) end
46-
if fields.radius and tonumber(fields.radius) then meta:set_int("radius",math.max(math.min(10,fields.radius),1)) end
84+
if fields.channel then
85+
meta:set_string("channel", fields.channel)
86+
end
87+
if fields.radius then
88+
local value = math.max(1, math.min(10, tonumber(fields.radius) or 1))
89+
meta:set_int("radius", value)
90+
end
91+
if fields.distance then
92+
local value = math.max(0, math.min(20, tonumber(fields.distance) or 0))
93+
meta:set_int("distance", value)
94+
end
95+
if fields.enable then
96+
meta:set_string("formspec", get_formspec(true))
97+
minetest.get_node_timer(pos):start(1)
98+
elseif fields.disable then
99+
meta:set_string("formspec", get_formspec(false))
100+
minetest.get_node_timer(pos):stop()
101+
end
47102
end,
48-
sounds = default and default.node_sound_stone_defaults()
103+
on_timer = search_for_players,
104+
digiline = {
105+
receptor = {},
106+
effector = {
107+
action = function(pos, node, channel, msg)
108+
local meta = minetest.get_meta(pos)
109+
if channel ~= meta:get_string("channel") then return end
110+
if type(msg) == "table" then
111+
if msg.radius then
112+
local value = math.max(1, math.min(10, tonumber(msg.radius) or 1))
113+
meta:set_int("radius", value)
114+
end
115+
if msg.distance then
116+
local value = math.max(0, math.min(20, tonumber(msg.distance) or 0))
117+
meta:set_int("distance", value)
118+
end
119+
if msg.command == "get" then
120+
search_for_players(pos, true)
121+
end
122+
elseif msg == "GET" or msg == "get" then
123+
search_for_players(pos, true)
124+
end
125+
end,
126+
},
127+
},
128+
_digistuff_channelcopier_fieldname = "channel",
49129
})
50130

51-
minetest.register_abm({
131+
minetest.register_lbm({
132+
label = "Digistuff camera update",
133+
name = "digistuff:camera_update",
52134
nodenames = {"digistuff:camera"},
53-
interval = 1.0,
54-
chance = 1,
55-
action = function(pos,node)
56-
local meta = minetest.get_meta(pos)
57-
local channel = meta:get_string("channel")
58-
local radius = meta:get_int("radius")
59-
local distance = meta:get_int("distance")
60-
local dir = vector.multiply(minetest.facedir_to_dir(node.param2),-1)
61-
local spot = vector.add(pos,vector.multiply(dir,distance))
62-
local i = 0
63-
while i <= 10 and minetest.get_node(spot).name == "air" do
64-
--Downward search for ground level
65-
spot = vector.add(spot,vector.new(0,-1,0))
66-
i = i + 1
67-
end
68-
if minetest.get_node(spot).name == "air" or minetest.get_node(spot).name == "ignore" then
69-
--Ground not in range
70-
return
71-
end
72-
73-
local found_any = false
74-
local players_found = {}
75-
local objs = minetest.get_objects_inside_radius(spot,radius)
76-
if objs then
77-
for _,obj in ipairs(objs) do
78-
if obj:is_player() then
79-
table.insert(players_found,obj:get_player_name())
80-
found_any = true
81-
end
82-
end
83-
if found_any then
84-
digilines.receptor_send({x=pos.x,y=pos.y-1,z=pos.z}, digilines.rules.default, channel, players_found)
85-
end
86-
end
135+
run_at_every_load = false,
136+
action = function(pos)
137+
local meta = minetest.get_meta(pos)
138+
if not meta:get("radius") then
139+
meta:set_int("radius", 1)
140+
end
141+
if not meta:get("distance") then
142+
meta:set_int("distance", 0)
87143
end
144+
meta:set_string("formspec", get_formspec(true))
145+
minetest.get_node_timer(pos):start(1)
146+
end,
88147
})
89148

90149
minetest.register_craft({

detector.lua

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,103 @@
1+
2+
local formspec = "size[8,4]"..
3+
"field[1.3,1;6,1;channel;Channel;${channel}]"..
4+
"field[1.3,2;6,1.3;radius;Radius;${radius}]"..
5+
"button_exit[4,3;3,1;submit;Save]"
6+
7+
local function get_formspec(enabled)
8+
if enabled then
9+
return formspec.."button[1,3;3,1;disable;Disable]"
10+
else
11+
return formspec.."button[1,3;3,1;enable;Enable]"
12+
end
13+
end
14+
15+
local function search_for_players(pos, send_empty)
16+
local meta = minetest.get_meta(pos)
17+
local radius = meta:get_int("radius")
18+
local found = {}
19+
for _,player in pairs(minetest.get_connected_players()) do
20+
if vector.distance(pos, player:get_pos()) <= radius then
21+
table.insert(found, player:get_player_name())
22+
end
23+
end
24+
if #found > 0 or send_empty == true then
25+
local channel = meta:get_string("channel")
26+
digilines.receptor_send(pos, digilines.rules.default, channel, found)
27+
end
28+
return true
29+
end
30+
131
minetest.register_node("digistuff:detector", {
32+
description = "Digilines Player Detector",
233
tiles = {
3-
"digistuff_digidetector.png"
34+
"digistuff_digidetector.png"
435
},
5-
digiline = {
6-
receptor = {}
7-
},
8-
groups = {cracky=2},
9-
description = "Digilines Player Detector",
36+
sounds = default and default.node_sound_stone_defaults(),
37+
groups = {cracky = 2},
1038
on_construct = function(pos)
1139
local meta = minetest.get_meta(pos)
12-
meta:set_string("formspec","size[8,4;]field[1,1;6,2;channel;Channel;${channel}]field[1,2;6,2;radius;Radius;${radius}]button_exit[2.25,3;3,1;submit;Save]")
40+
meta:set_string("formspec", get_formspec(true))
41+
meta:set_int("radius", 6)
42+
minetest.get_node_timer(pos):start(1)
1343
end,
14-
_digistuff_channelcopier_fieldname = "channel",
15-
on_receive_fields = function(pos, formname, fields, sender)
16-
local name = sender:get_player_name()
17-
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
18-
minetest.record_protection_violation(pos,name)
44+
on_receive_fields = function(pos, _, fields, player)
45+
if minetest.is_protected(pos, player:get_player_name()) then
1946
return
2047
end
2148
local meta = minetest.get_meta(pos)
22-
if fields.channel then meta:set_string("channel",fields.channel) end
23-
if fields.msg then meta:set_string("msg",fields.msg) end
24-
if fields.radius then meta:set_string("radius",fields.radius) end
49+
if fields.channel then
50+
meta:set_string("channel", fields.channel)
51+
end
52+
if fields.radius then
53+
local value = math.max(1, math.min(10, tonumber(fields.radius) or 6))
54+
meta:set_int("radius", value)
55+
end
56+
if fields.enable then
57+
meta:set_string("formspec", get_formspec(true))
58+
minetest.get_node_timer(pos):start(1)
59+
elseif fields.disable then
60+
meta:set_string("formspec", get_formspec(false))
61+
minetest.get_node_timer(pos):stop()
62+
end
2563
end,
26-
sounds = default and default.node_sound_stone_defaults()
64+
on_timer = search_for_players,
65+
digiline = {
66+
receptor = {},
67+
effector = {
68+
action = function(pos, node, channel, msg)
69+
local meta = minetest.get_meta(pos)
70+
if channel ~= meta:get_string("channel") then return end
71+
if type(msg) == "table" then
72+
if msg.radius then
73+
local value = math.max(1, math.min(10, tonumber(msg.radius) or 1))
74+
meta:set_int("radius", value)
75+
end
76+
if msg.command == "get" then
77+
search_for_players(pos, true)
78+
end
79+
elseif msg == "GET" or msg == "get" then
80+
search_for_players(pos, true)
81+
end
82+
end,
83+
},
84+
},
85+
_digistuff_channelcopier_fieldname = "channel",
2786
})
2887

29-
minetest.register_abm({
88+
minetest.register_lbm({
89+
label = "Digistuff detector update",
90+
name = "digistuff:detector_update",
3091
nodenames = {"digistuff:detector"},
31-
interval = 1.0,
32-
chance = 1,
92+
run_at_every_load = false,
3393
action = function(pos)
34-
local meta = minetest.get_meta(pos)
35-
local channel = meta:get_string("channel")
36-
local radius = meta:get_string("radius")
37-
local found_any = false
38-
local players_found = {}
39-
if not radius or not tonumber(radius) or tonumber(radius) < 1 or tonumber(radius) > 10 then radius = 6 end
40-
local objs = minetest.get_objects_inside_radius(pos, radius)
41-
if objs then
42-
for _,obj in ipairs(objs) do
43-
if obj:is_player() then
44-
table.insert(players_found,obj:get_player_name())
45-
found_any = true
46-
end
47-
end
48-
if found_any then
49-
digilines.receptor_send(pos, digilines.rules.default, channel, players_found)
50-
end
51-
end
94+
local meta = minetest.get_meta(pos)
95+
if not meta:get("radius") then
96+
meta:set_int("radius", 6)
5297
end
98+
meta:set_string("formspec", get_formspec(true))
99+
minetest.get_node_timer(pos):start(1)
100+
end,
53101
})
54102

55103
minetest.register_craft({

docs/camera.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Digilines Camera
2+
3+
The camera looks at a node in front of it, detecting players within a radius of that node, and sending a resulting list of names via digilines.
4+
5+
## Basic usage
6+
7+
Set a channel, radius and distance. The radius must be between 1 and 10, and the distance must be between 0 and 20.
8+
9+
The camera will search for a node "distance" nodes in front of it and up to 10 nodes down. Every second while a player is within the radius of that node, a table listing the players in range will be sent via digilines on the chosen channel.
10+
11+
This functionality can be toggled by clicking the "Disable" or "Enable" button.
12+
13+
## Commands
14+
15+
Similar to the `mesecons` node detector, a digiline command can be sent to retrieve the list of players on demand.
16+
17+
```lua
18+
digiline_send("detector", {command = "get"})
19+
20+
-- A string also works
21+
digiline_send("detector", "get")
22+
```
23+
24+
The radius and distance can also be set by digilines.
25+
26+
```lua
27+
digiline_send("detector", {radius = 1, distance = 0})
28+
```

docs/camera.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)