Vanessa Dannenberg
2018-10-31 44cb8df048e09b64214f59db73a3fd23cfe12e77
commit | author | age
30a37a 1 --- Forcefield generator.
S 2 -- @author ShadowNinja
ee0765 3 --
S 4 -- Forcefields are powerful barriers but they consume huge amounts of power.
30a37a 5 -- The forcefield Generator is an HV machine.
ee0765 6
S 7 -- How expensive is the generator?
8 -- Leaves room for upgrades lowering the power drain?
ef8bb3 9 local digilines_path = minetest.get_modpath("digilines")
D 10
ee0765 11 local forcefield_power_drain   = 10
S 12
be2f30 13 local S = technic.getter
S 14
54004f 15 local cable_entry = "^technic_cable_connection_overlay.png"
VE 16
ee0765 17 minetest.register_craft({
30a37a 18     output = "technic:forcefield_emitter_off",
ee0765 19     recipe = {
44cb8d 20         {"default:mese",         "basic_materials:motor",          "default:mese"        },
51f9df 21         {"technic:deployer_off", "technic:machine_casing", "technic:deployer_off"},
D 22         {"default:mese",         "technic:hv_cable",       "default:mese"        },
ee0765 23     }
S 24 })
30a37a 25
S 26
27 local replaceable_cids = {}
28
29 minetest.after(0, function()
30     for name, ndef in pairs(minetest.registered_nodes) do
31         if ndef.buildable_to == true and name ~= "ignore" then
32             replaceable_cids[minetest.get_content_id(name)] = true
33         end
34     end
35 end)
ee0765 36
S 37
38 -- Idea: Let forcefields have different colors by upgrade slot.
39 -- Idea: Let forcefields add up by detecting if one hits another.
40 --    ___   __
41 --   /   \/   \
42 --  |          |
43 --   \___/\___/
44
30a37a 45 local function update_forcefield(pos, meta, active, first)
830de4 46     local shape = meta:get_int("shape")
Z 47     local range = meta:get_int("range")
ee0765 48     local vm = VoxelManip()
30a37a 49     local MinEdge, MaxEdge = vm:read_from_map(vector.subtract(pos, range),
S 50             vector.add(pos, range))
ee0765 51     local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
S 52     local data = vm:get_data()
53
30a37a 54     local c_air = minetest.get_content_id("air")
ee0765 55     local c_field = minetest.get_content_id("technic:forcefield")
S 56
30a37a 57     for z = -range, range do
S 58     for y = -range, range do
59     local vi = area:index(pos.x + (-range), pos.y + y, pos.z + z)
60     for x = -range, range do
830de4 61         local relevant
Z 62         if shape == 0 then
30a37a 63             local squared = x * x + y * y + z * z
830de4 64             relevant =
30a37a 65                 squared <= range       *  range      +  range and
S 66                 squared >= (range - 1) * (range - 1) + (range - 1)
830de4 67         else
Z 68             relevant =
69                 x == -range or x == range or
70                 y == -range or y == range or
71                 z == -range or z == range
72         end
73         if relevant then
30a37a 74             local cid = data[vi]
S 75             if active and replaceable_cids[cid] then
ee0765 76                 data[vi] = c_field
30a37a 77             elseif not active and cid == c_field then
ee0765 78                 data[vi] = c_air
S 79             end
80         end
81         vi = vi + 1
82     end
83     end
84     end
85
86     vm:set_data(data)
87     vm:update_liquids()
88     vm:write_to_map()
30a37a 89     -- update_map is very slow, but if we don't call it we'll
S 90     -- get phantom blocks on the client.
91     if not active or first then
92         vm:update_map()
93     end
ee0765 94 end
S 95
2d6f34 96 local function set_forcefield_formspec(meta)
ef8bb3 97     local formspec
D 98     if digilines_path then
99         formspec = "size[5,3.25]"..
100             "field[0.3,3;5,1;channel;Digiline Channel;"..meta:get_string("channel").."]"
101     else
102         formspec = "size[5,2.25]"
103     end
104     formspec = formspec..
830de4 105         "field[0.3,0.5;2,1;range;"..S("Range")..";"..meta:get_int("range").."]"
cca72f 106     -- The names for these toggle buttons are explicit about which
Z 107     -- state they'll switch to, so that multiple presses (arising
108     -- from the ambiguity between lag and a missed press) only make
109     -- the single change that the user expects.
830de4 110     if meta:get_int("shape") == 0 then
Z 111         formspec = formspec.."button[3,0.2;2,1;shape1;"..S("Sphere").."]"
112     else
113         formspec = formspec.."button[3,0.2;2,1;shape0;"..S("Cube").."]"
114     end
6a4cb1 115     if meta:get_int("mesecon_mode") == 0 then
cca72f 116         formspec = formspec.."button[0,1;5,1;mesecon_mode_1;"..S("Ignoring Mesecon Signal").."]"
2d6f34 117     else
cca72f 118         formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
6a4cb1 119     end
Z 120     if meta:get_int("enabled") == 0 then
cca72f 121         formspec = formspec.."button[0,1.75;5,1;enable;"..S("%s Disabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
6a4cb1 122     else
cca72f 123         formspec = formspec.."button[0,1.75;5,1;disable;"..S("%s Enabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
2d6f34 124     end
Z 125     meta:set_string("formspec", formspec)
ee0765 126 end
S 127
128 local forcefield_receive_fields = function(pos, formname, fields, sender)
129     local meta = minetest.get_meta(pos)
830de4 130     local range = nil
2d6f34 131     if fields.range then
830de4 132         range = tonumber(fields.range) or 0
2d6f34 133         -- Smallest field is 5. Anything less is asking for trouble.
Z 134         -- Largest is 20. It is a matter of pratical node handling.
135         -- At the maximim range updating the forcefield takes about 0.2s
136         range = math.max(range, 5)
137         range = math.min(range, 20)
830de4 138         if range == meta:get_int("range") then range = nil end
ee0765 139     end
830de4 140     if fields.shape0 or fields.shape1 or range then
Z 141         update_forcefield(pos, meta, false)
142     end
143     if range then meta:set_int("range", range) end
ef8bb3 144     if fields.channel then meta:set_string("channel", fields.channel) end
D 145     if fields.shape0  then meta:set_int("shape", 0) end
146     if fields.shape1  then meta:set_int("shape", 1) end
147     if fields.enable  then meta:set_int("enabled", 1) end
2d6f34 148     if fields.disable then meta:set_int("enabled", 0) end
6a4cb1 149     if fields.mesecon_mode_0 then meta:set_int("mesecon_mode", 0) end
Z 150     if fields.mesecon_mode_1 then meta:set_int("mesecon_mode", 1) end
2d6f34 151     set_forcefield_formspec(meta)
ee0765 152 end
S 153
154 local mesecons = {
155     effector = {
156         action_on = function(pos, node)
6a4cb1 157             minetest.get_meta(pos):set_int("mesecon_effect", 1)
ee0765 158         end,
S 159         action_off = function(pos, node)
6a4cb1 160             minetest.get_meta(pos):set_int("mesecon_effect", 0)
ee0765 161         end
S 162     }
ef8bb3 163 }
D 164
165 local digiline_def = {
166     receptor = {action = function() end},
167     effector = {
168         action = function(pos, node, channel, msg)
169             local meta = minetest.get_meta(pos)
170             if channel ~= meta:get_string("channel") then
171                 return
172             end
51f9df 173             local msgt = type(msg)
D 174             if msgt == "string" then
175                 local smsg = msg:lower()
176                 msg = {}
177                 if smsg == "get" then
178                     msg.command = "get"
179                 elseif smsg == "off" then
180                     msg.command = "off"
181                 elseif smsg == "on" then
182                     msg.command = "on"
183                 elseif smsg == "toggle" then
184                     msg.command = "toggle"
185                 elseif smsg:sub(1, 5) == "range" then
186                     msg.command = "range"
187                     msg.value = tonumber(smsg:sub(7))
188                 elseif smsg:sub(1, 5) == "shape" then
189                     msg.command = "shape"
190                     msg.value = smsg:sub(7):lower()
191                     msg.value = tonumber(msg.value) or msg.value
192                 end
193             elseif msgt ~= "table" then
194                 return
195             end
196             if msg.command == "get" then
ef8bb3 197                 digilines.receptor_send(pos, digilines.rules.default, channel, {
D 198                     enabled = meta:get_int("enabled"),
199                     range   = meta:get_int("range"),
200                     shape   = meta:get_int("shape")
201                 })
202                 return
51f9df 203             elseif msg.command == "off" then
ef8bb3 204                 meta:set_int("enabled", 0)
51f9df 205             elseif msg.command == "on" then
ef8bb3 206                 meta:set_int("enabled", 1)
51f9df 207             elseif msg.command == "toggle" then
ef8bb3 208                 local onn = meta:get_int("enabled")
D 209                 onn = 1-onn -- Mirror onn with pivot 0.5, so switch between 1 and 0.
210                 meta:set_int("enabled", onn)
51f9df 211             elseif msg.command == "range" then
D 212                 if type(msg.value) ~= "number" then
ef8bb3 213                     return
D 214                 end
51f9df 215                 msg.value = math.max(msg.value, 5)
D 216                 msg.value = math.min(msg.value, 20)
ef8bb3 217                 update_forcefield(pos, meta, false)
51f9df 218                 meta:set_int("range", msg.value)
D 219             elseif msg.command == "shape" then
220                 local valuet = type(msg.value)
221                 if valuet == "string" then
222                     if msg.value == "sphere" then
223                         msg.value = 0
224                     elseif msg.value == "cube" then
225                         msg.value = 1
226                     end
227                 elseif valuet ~= "number" then
228                     return
ef8bb3 229                 end
51f9df 230                 if not msg.value then
ef8bb3 231                     return
D 232                 end
233                 update_forcefield(pos, meta, false)
51f9df 234                 meta:set_int("shape", msg.value)
ef8bb3 235             else
D 236                 return
237             end
238             set_forcefield_formspec(meta)
239         end
240     },
ee0765 241 }
S 242
30a37a 243 local function run(pos, node)
563a4c 244     local meta = minetest.get_meta(pos)
N 245     local eu_input   = meta:get_int("HV_EU_input")
51f9df 246     local enabled = meta:get_int("enabled") ~= 0 and
D 247         (meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
563a4c 248     local machine_name = S("%s Forcefield Emitter"):format("HV")
N 249
830de4 250     local range = meta:get_int("range")
Z 251     local power_requirement
252     if meta:get_int("shape") == 0 then
253         power_requirement = math.floor(4 * math.pi * range * range)
254     else
255         power_requirement = 24 * range * range
256     end
257     power_requirement = power_requirement * forcefield_power_drain
563a4c 258
6a4cb1 259     if not enabled then
563a4c 260         if node.name == "technic:forcefield_emitter_on" then
830de4 261             update_forcefield(pos, meta, false)
563a4c 262             technic.swap_node(pos, "technic:forcefield_emitter_off")
N 263             meta:set_string("infotext", S("%s Disabled"):format(machine_name))
264         end
849526 265         meta:set_int("HV_EU_demand", 0)
Z 266         return
267     end
268     meta:set_int("HV_EU_demand", power_requirement)
269     if eu_input < power_requirement then
563a4c 270         meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
N 271         if node.name == "technic:forcefield_emitter_on" then
830de4 272             update_forcefield(pos, meta, false)
563a4c 273             technic.swap_node(pos, "technic:forcefield_emitter_off")
N 274         end
275     elseif eu_input >= power_requirement then
30a37a 276         local first = false
563a4c 277         if node.name == "technic:forcefield_emitter_off" then
30a37a 278             first = true
563a4c 279             technic.swap_node(pos, "technic:forcefield_emitter_on")
N 280             meta:set_string("infotext", S("%s Active"):format(machine_name))
281         end
30a37a 282         update_forcefield(pos, meta, true, first)
563a4c 283     end
N 284 end
285
ee0765 286 minetest.register_node("technic:forcefield_emitter_off", {
7c4b70 287     description = S("%s Forcefield Emitter"):format("HV"),
54004f 288     tiles = {
VE 289         "technic_forcefield_emitter_off.png",
290         "technic_machine_bottom.png"..cable_entry,
291         "technic_forcefield_emitter_off.png",
292         "technic_forcefield_emitter_off.png",
293         "technic_forcefield_emitter_off.png",
294         "technic_forcefield_emitter_off.png"
295     },
83c649 296     groups = {cracky = 1, technic_machine = 1, technic_hv = 1},
ee0765 297     on_receive_fields = forcefield_receive_fields,
S 298     on_construct = function(pos)
299         local meta = minetest.get_meta(pos)
300         meta:set_int("HV_EU_input", 0)
301         meta:set_int("HV_EU_demand", 0)
302         meta:set_int("range", 10)
303         meta:set_int("enabled", 0)
6a4cb1 304         meta:set_int("mesecon_mode", 0)
Z 305         meta:set_int("mesecon_effect", 0)
ef8bb3 306         if digilines_path then
D 307             meta:set_string("channel", "forcefield"..minetest.pos_to_string(pos))
308         end
7c4b70 309         meta:set_string("infotext", S("%s Forcefield Emitter"):format("HV"))
2d6f34 310         set_forcefield_formspec(meta)
ee0765 311     end,
563a4c 312     mesecons = mesecons,
ef8bb3 313     digiline = digiline_def,
563a4c 314     technic_run = run,
ee0765 315 })
S 316
317 minetest.register_node("technic:forcefield_emitter_on", {
7c4b70 318     description = S("%s Forcefield Emitter"):format("HV"),
54004f 319     tiles = {
VE 320         "technic_forcefield_emitter_on.png",
321         "technic_machine_bottom.png"..cable_entry,
322         "technic_forcefield_emitter_on.png",
323         "technic_forcefield_emitter_on.png",
324         "technic_forcefield_emitter_on.png",
325         "technic_forcefield_emitter_on.png"
326     },
83c649 327     groups = {cracky = 1, technic_machine = 1, technic_hv = 1,
S 328             not_in_creative_inventory=1},
ee0765 329     drop = "technic:forcefield_emitter_off",
S 330     on_receive_fields = forcefield_receive_fields,
331     on_destruct = function(pos)
332         local meta = minetest.get_meta(pos)
830de4 333         update_forcefield(pos, meta, false)
ee0765 334     end,
563a4c 335     mesecons = mesecons,
ef8bb3 336     digiline = digiline_def,
563a4c 337     technic_run = run,
2a7ee1 338     technic_on_disable = function (pos, node)
Z 339         local meta = minetest.get_meta(pos)
830de4 340         update_forcefield(pos, meta, false)
2a7ee1 341         technic.swap_node(pos, "technic:forcefield_emitter_off")
Z 342     end,
51f9df 343     on_blast = function(pos, intensity)
D 344         minetest.dig_node(pos)
345         return {"technic:forcefield_emitter_off"}
346     end,
ee0765 347 })
S 348
349 minetest.register_node("technic:forcefield", {
7c4b70 350     description = S("%s Forcefield"):format("HV"),
ee0765 351     sunlight_propagates = true,
S 352     drawtype = "glasslike",
45919b 353     groups = {not_in_creative_inventory=1},
ee0765 354     paramtype = "light",
51f9df 355     light_source = default.LIGHT_MAX,
45919b 356     diggable = false,
ee0765 357     drop = '',
S 358     tiles = {{
359         name = "technic_forcefield_animated.png",
360         animation = {
361             type = "vertical_frames",
362             aspect_w = 16,
363             aspect_h = 16,
364             length = 1.0,
365         },
366     }},
51f9df 367     on_blast = function(pos, intensity)
D 368     end,
ee0765 369 })
S 370
371
372 if minetest.get_modpath("mesecons_mvps") then
8da4d0 373     mesecon.register_mvps_stopper("technic:forcefield")
ee0765 374 end
S 375
376 technic.register_machine("HV", "technic:forcefield_emitter_on",  technic.receiver)
377 technic.register_machine("HV", "technic:forcefield_emitter_off", technic.receiver)
378