Sires
2018-03-15 707fa5a97c0d56e18f16d56f7130ff6220a090a1
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
aa82fa 45 local function update_forcefield(pos, meta, active)
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()
89 end
90
2d6f34 91 local function set_forcefield_formspec(meta)
ef8bb3 92     local formspec
D 93     if digilines_path then
94         formspec = "size[5,3.25]"..
95             "field[0.3,3;5,1;channel;Digiline Channel;"..meta:get_string("channel").."]"
96     else
97         formspec = "size[5,2.25]"
98     end
99     formspec = formspec..
830de4 100         "field[0.3,0.5;2,1;range;"..S("Range")..";"..meta:get_int("range").."]"
cca72f 101     -- The names for these toggle buttons are explicit about which
Z 102     -- state they'll switch to, so that multiple presses (arising
103     -- from the ambiguity between lag and a missed press) only make
104     -- the single change that the user expects.
830de4 105     if meta:get_int("shape") == 0 then
Z 106         formspec = formspec.."button[3,0.2;2,1;shape1;"..S("Sphere").."]"
107     else
108         formspec = formspec.."button[3,0.2;2,1;shape0;"..S("Cube").."]"
109     end
6a4cb1 110     if meta:get_int("mesecon_mode") == 0 then
cca72f 111         formspec = formspec.."button[0,1;5,1;mesecon_mode_1;"..S("Ignoring Mesecon Signal").."]"
2d6f34 112     else
cca72f 113         formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
6a4cb1 114     end
Z 115     if meta:get_int("enabled") == 0 then
cca72f 116         formspec = formspec.."button[0,1.75;5,1;enable;"..S("%s Disabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
6a4cb1 117     else
cca72f 118         formspec = formspec.."button[0,1.75;5,1;disable;"..S("%s Enabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
2d6f34 119     end
Z 120     meta:set_string("formspec", formspec)
ee0765 121 end
S 122
123 local forcefield_receive_fields = function(pos, formname, fields, sender)
b81d1d 124     local player_name = sender:get_player_name()
L 125     if minetest.is_protected(pos, player_name) then
126         minetest.chat_send_player(player_name, "You are not allowed to edit this!")
127         minetest.record_protection_violation(pos, player_name)
128         return
129     end
ee0765 130     local meta = minetest.get_meta(pos)
830de4 131     local range = nil
2d6f34 132     if fields.range then
830de4 133         range = tonumber(fields.range) or 0
2d6f34 134         -- Smallest field is 5. Anything less is asking for trouble.
Z 135         -- Largest is 20. It is a matter of pratical node handling.
136         -- At the maximim range updating the forcefield takes about 0.2s
137         range = math.max(range, 5)
138         range = math.min(range, 20)
830de4 139         if range == meta:get_int("range") then range = nil end
ee0765 140     end
830de4 141     if fields.shape0 or fields.shape1 or range then
Z 142         update_forcefield(pos, meta, false)
143     end
144     if range then meta:set_int("range", range) end
ef8bb3 145     if fields.channel then meta:set_string("channel", fields.channel) end
D 146     if fields.shape0  then meta:set_int("shape", 0) end
147     if fields.shape1  then meta:set_int("shape", 1) end
148     if fields.enable  then meta:set_int("enabled", 1) end
2d6f34 149     if fields.disable then meta:set_int("enabled", 0) end
6a4cb1 150     if fields.mesecon_mode_0 then meta:set_int("mesecon_mode", 0) end
Z 151     if fields.mesecon_mode_1 then meta:set_int("mesecon_mode", 1) end
2d6f34 152     set_forcefield_formspec(meta)
ee0765 153 end
S 154
155 local mesecons = {
156     effector = {
157         action_on = function(pos, node)
6a4cb1 158             minetest.get_meta(pos):set_int("mesecon_effect", 1)
ee0765 159         end,
S 160         action_off = function(pos, node)
6a4cb1 161             minetest.get_meta(pos):set_int("mesecon_effect", 0)
ee0765 162         end
S 163     }
ef8bb3 164 }
D 165
166 local digiline_def = {
167     receptor = {action = function() end},
168     effector = {
169         action = function(pos, node, channel, msg)
170             local meta = minetest.get_meta(pos)
171             if channel ~= meta:get_string("channel") then
172                 return
173             end
51f9df 174             local msgt = type(msg)
D 175             if msgt == "string" then
176                 local smsg = msg:lower()
177                 msg = {}
178                 if smsg == "get" then
179                     msg.command = "get"
180                 elseif smsg == "off" then
181                     msg.command = "off"
182                 elseif smsg == "on" then
183                     msg.command = "on"
184                 elseif smsg == "toggle" then
185                     msg.command = "toggle"
186                 elseif smsg:sub(1, 5) == "range" then
187                     msg.command = "range"
188                     msg.value = tonumber(smsg:sub(7))
189                 elseif smsg:sub(1, 5) == "shape" then
190                     msg.command = "shape"
191                     msg.value = smsg:sub(7):lower()
192                     msg.value = tonumber(msg.value) or msg.value
193                 end
194             elseif msgt ~= "table" then
195                 return
196             end
197             if msg.command == "get" then
ef8bb3 198                 digilines.receptor_send(pos, digilines.rules.default, channel, {
D 199                     enabled = meta:get_int("enabled"),
200                     range   = meta:get_int("range"),
201                     shape   = meta:get_int("shape")
202                 })
203                 return
51f9df 204             elseif msg.command == "off" then
ef8bb3 205                 meta:set_int("enabled", 0)
51f9df 206             elseif msg.command == "on" then
ef8bb3 207                 meta:set_int("enabled", 1)
51f9df 208             elseif msg.command == "toggle" then
ef8bb3 209                 local onn = meta:get_int("enabled")
D 210                 onn = 1-onn -- Mirror onn with pivot 0.5, so switch between 1 and 0.
211                 meta:set_int("enabled", onn)
51f9df 212             elseif msg.command == "range" then
D 213                 if type(msg.value) ~= "number" then
ef8bb3 214                     return
D 215                 end
51f9df 216                 msg.value = math.max(msg.value, 5)
D 217                 msg.value = math.min(msg.value, 20)
ef8bb3 218                 update_forcefield(pos, meta, false)
51f9df 219                 meta:set_int("range", msg.value)
D 220             elseif msg.command == "shape" then
221                 local valuet = type(msg.value)
222                 if valuet == "string" then
223                     if msg.value == "sphere" then
224                         msg.value = 0
225                     elseif msg.value == "cube" then
226                         msg.value = 1
227                     end
228                 elseif valuet ~= "number" then
229                     return
ef8bb3 230                 end
51f9df 231                 if not msg.value then
ef8bb3 232                     return
D 233                 end
234                 update_forcefield(pos, meta, false)
51f9df 235                 meta:set_int("shape", msg.value)
ef8bb3 236             else
D 237                 return
238             end
239             set_forcefield_formspec(meta)
240         end
241     },
ee0765 242 }
S 243
30a37a 244 local function run(pos, node)
563a4c 245     local meta = minetest.get_meta(pos)
N 246     local eu_input   = meta:get_int("HV_EU_input")
51f9df 247     local enabled = meta:get_int("enabled") ~= 0 and
D 248         (meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
563a4c 249     local machine_name = S("%s Forcefield Emitter"):format("HV")
N 250
830de4 251     local range = meta:get_int("range")
Z 252     local power_requirement
253     if meta:get_int("shape") == 0 then
254         power_requirement = math.floor(4 * math.pi * range * range)
255     else
256         power_requirement = 24 * range * range
257     end
258     power_requirement = power_requirement * forcefield_power_drain
563a4c 259
6a4cb1 260     if not enabled then
563a4c 261         if node.name == "technic:forcefield_emitter_on" then
830de4 262             update_forcefield(pos, meta, false)
563a4c 263             technic.swap_node(pos, "technic:forcefield_emitter_off")
N 264             meta:set_string("infotext", S("%s Disabled"):format(machine_name))
265         end
849526 266         meta:set_int("HV_EU_demand", 0)
Z 267         return
268     end
269     meta:set_int("HV_EU_demand", power_requirement)
270     if eu_input < power_requirement then
563a4c 271         meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
N 272         if node.name == "technic:forcefield_emitter_on" then
830de4 273             update_forcefield(pos, meta, false)
563a4c 274             technic.swap_node(pos, "technic:forcefield_emitter_off")
N 275         end
276     elseif eu_input >= power_requirement then
277         if node.name == "technic:forcefield_emitter_off" then
278             technic.swap_node(pos, "technic:forcefield_emitter_on")
279             meta:set_string("infotext", S("%s Active"):format(machine_name))
280         end
aa82fa 281         update_forcefield(pos, meta, true)
563a4c 282     end
N 283 end
284
ee0765 285 minetest.register_node("technic:forcefield_emitter_off", {
7c4b70 286     description = S("%s Forcefield Emitter"):format("HV"),
54004f 287     tiles = {
VE 288         "technic_forcefield_emitter_off.png",
289         "technic_machine_bottom.png"..cable_entry,
290         "technic_forcefield_emitter_off.png",
291         "technic_forcefield_emitter_off.png",
292         "technic_forcefield_emitter_off.png",
293         "technic_forcefield_emitter_off.png"
294     },
83c649 295     groups = {cracky = 1, technic_machine = 1, technic_hv = 1},
ee0765 296     on_receive_fields = forcefield_receive_fields,
S 297     on_construct = function(pos)
298         local meta = minetest.get_meta(pos)
299         meta:set_int("HV_EU_input", 0)
300         meta:set_int("HV_EU_demand", 0)
301         meta:set_int("range", 10)
302         meta:set_int("enabled", 0)
6a4cb1 303         meta:set_int("mesecon_mode", 0)
Z 304         meta:set_int("mesecon_effect", 0)
ef8bb3 305         if digilines_path then
D 306             meta:set_string("channel", "forcefield"..minetest.pos_to_string(pos))
307         end
7c4b70 308         meta:set_string("infotext", S("%s Forcefield Emitter"):format("HV"))
2d6f34 309         set_forcefield_formspec(meta)
ee0765 310     end,
563a4c 311     mesecons = mesecons,
ef8bb3 312     digiline = digiline_def,
563a4c 313     technic_run = run,
ee0765 314 })
S 315
316 minetest.register_node("technic:forcefield_emitter_on", {
7c4b70 317     description = S("%s Forcefield Emitter"):format("HV"),
54004f 318     tiles = {
VE 319         "technic_forcefield_emitter_on.png",
320         "technic_machine_bottom.png"..cable_entry,
321         "technic_forcefield_emitter_on.png",
322         "technic_forcefield_emitter_on.png",
323         "technic_forcefield_emitter_on.png",
324         "technic_forcefield_emitter_on.png"
325     },
83c649 326     groups = {cracky = 1, technic_machine = 1, technic_hv = 1,
S 327             not_in_creative_inventory=1},
ee0765 328     drop = "technic:forcefield_emitter_off",
S 329     on_receive_fields = forcefield_receive_fields,
330     on_destruct = function(pos)
331         local meta = minetest.get_meta(pos)
830de4 332         update_forcefield(pos, meta, false)
ee0765 333     end,
563a4c 334     mesecons = mesecons,
ef8bb3 335     digiline = digiline_def,
563a4c 336     technic_run = run,
2a7ee1 337     technic_on_disable = function (pos, node)
Z 338         local meta = minetest.get_meta(pos)
830de4 339         update_forcefield(pos, meta, false)
2a7ee1 340         technic.swap_node(pos, "technic:forcefield_emitter_off")
Z 341     end,
51f9df 342     on_blast = function(pos, intensity)
D 343         minetest.dig_node(pos)
344         return {"technic:forcefield_emitter_off"}
345     end,
ee0765 346 })
S 347
348 minetest.register_node("technic:forcefield", {
7c4b70 349     description = S("%s Forcefield"):format("HV"),
ee0765 350     sunlight_propagates = true,
S 351     drawtype = "glasslike",
45919b 352     groups = {not_in_creative_inventory=1},
ee0765 353     paramtype = "light",
51f9df 354     light_source = default.LIGHT_MAX,
45919b 355     diggable = false,
ee0765 356     drop = '',
S 357     tiles = {{
358         name = "technic_forcefield_animated.png",
359         animation = {
360             type = "vertical_frames",
361             aspect_w = 16,
362             aspect_h = 16,
363             length = 1.0,
364         },
365     }},
51f9df 366     on_blast = function(pos, intensity)
D 367     end,
ee0765 368 })
S 369
370
371 if minetest.get_modpath("mesecons_mvps") then
8da4d0 372     mesecon.register_mvps_stopper("technic:forcefield")
ee0765 373 end
S 374
375 technic.register_machine("HV", "technic:forcefield_emitter_on",  technic.receiver)
376 technic.register_machine("HV", "technic:forcefield_emitter_off", technic.receiver)
377