Add mods: technic, moreores, paintings, Nyancat (Pbj_pup). Small fix: sandwiches

This commit is contained in:
N-Nachtigal 2025-06-05 16:15:56 +02:00
parent 15e8e696a2
commit fb09deddc1
1404 changed files with 156555 additions and 211 deletions

View file

@ -0,0 +1,21 @@
-- HV battery box
minetest.register_craft({
output = 'technic:hv_battery_box0',
recipe = {
{'technic:mv_battery_box0', 'technic:mv_battery_box0', 'technic:mv_battery_box0'},
{'technic:mv_battery_box0', 'technic:hv_transformer', 'technic:mv_battery_box0'},
{'', 'technic:hv_cable', ''},
}
})
technic.register_battery_box("technic:hv_battery_box", {
tier = "HV",
max_charge = 1000000,
charge_rate = 100000,
discharge_rate = 400000,
charge_step = 10000,
discharge_step = 40000,
upgrade = 1,
tube = 1,
})

View file

@ -0,0 +1,54 @@
local S = technic.getter
minetest.register_craft({
output = 'technic:hv_cable 3',
recipe = {
{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
{'technic:mv_cable', 'technic:mv_cable', 'technic:mv_cable'},
{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
}
})
minetest.register_craft({
output = "technic:hv_cable_plate_1 5",
recipe = {
{"" , "" , "technic:hv_cable"},
{"technic:hv_cable", "technic:hv_cable", "technic:hv_cable"},
{"" , "" , "technic:hv_cable"},
}
})
minetest.register_craft({
output = "technic:hv_cable",
recipe = {{"technic:hv_cable_plate_1"}}
})
-- Register cables
technic.register_cable("technic:hv_cable", {
tier = "HV",
size = 3/16,
description = S("@1 Cable", S("HV"))
})
technic.register_cable_plate("technic:hv_cable_plate", {
tier = "HV",
size = 3/16,
description = S("@1 Cable Plate", S("HV")),
tiles = {"technic_hv_cable.png"},
})
if minetest.get_modpath("digilines") then
technic.register_cable("technic:hv_digi_cable", {
tier = "HV",
size = 3/16,
description = S("@1 Digiline Cable", S("HV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } }
})
technic.register_cable_plate("technic:hv_digi_cable_plate", {
tier = "HV",
size = 3/16,
description = S("@1 Digiline Cable Plate", S("HV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } },
tiles = {"technic_hv_digi_cable.png"}
})
end

View file

@ -0,0 +1,21 @@
-- HV compressor
local S = technic.getter
minetest.register_craft({
output = 'technic:hv_compressor',
recipe = {
{'technic:carbon_plate', 'technic:mv_compressor', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:hv_compressor", {
typename = "compressing",
description = S("@1 Compressor", S("HV")),
tier = "HV",
demand = {1500, 1000, 750},
speed = 5,
upgrade = 1,
tube = 1
})

View file

@ -0,0 +1,26 @@
-- MV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
-- In addition to this it can be upgraded with microcontrollers and batteries
-- This new version uses the batteries to lower the power consumption of the machine
-- Also in addition this furnace can be attached to the pipe system from the pipeworks mod.
local S = technic.getter
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:hv_electric_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:mv_electric_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:hv_electric_furnace", {
typename = "cooking",
description = S("@1 Furnace", S("HV")),
tier="HV",
upgrade=1,
tube=1,
demand={4000, 2500, 1500},
speed=12
})

View file

@ -0,0 +1,400 @@
--- Forcefield generator.
-- @author ShadowNinja
--
-- Forcefields are powerful barriers but they consume huge amounts of power.
-- The forcefield Generator is an HV machine.
-- How expensive is the generator?
-- Leaves room for upgrades lowering the power drain?
local digilines_path = minetest.get_modpath("digilines")
local forcefield_power_drain = 10
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
local mat = technic.materials
minetest.register_craft({
output = "technic:forcefield_emitter_off",
recipe = {
{mat.mese, "basic_materials:motor", mat.mese },
{"technic:deployer_off", "technic:machine_casing", "technic:deployer_off"},
{mat.mese, "technic:hv_cable", mat.mese },
}
})
local replaceable_cids = {}
minetest.after(0, function()
for name, ndef in pairs(minetest.registered_nodes) do
if ndef.buildable_to == true and name ~= "ignore" then
replaceable_cids[minetest.get_content_id(name)] = true
end
end
end)
-- Idea: Let forcefields have different colors by upgrade slot.
-- Idea: Let forcefields add up by detecting if one hits another.
-- ___ __
-- / \/ \
-- | |
-- \___/\___/
local function update_forcefield(pos, meta, active)
if active then
-- rate limit by chance
if math.floor(math.random()*4) ~= 0 then
return
end
end
local shape = meta:get_int("shape")
local range = meta:get_int("range")
local vm = VoxelManip()
local MinEdge, MaxEdge = vm:read_from_map(vector.subtract(pos, range),
vector.add(pos, range))
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
local data = vm:get_data()
local c_air = minetest.get_content_id("air")
local c_field = minetest.get_content_id("technic:forcefield")
for z = -range, range do
for y = -range, range do
local vi = area:index(pos.x + (-range), pos.y + y, pos.z + z)
for x = -range, range do
local relevant
if shape == 0 then
local squared = x * x + y * y + z * z
relevant =
squared <= range * range + range and
squared >= (range - 1) * (range - 1) + (range - 1)
else
relevant =
x == -range or x == range or
y == -range or y == range or
z == -range or z == range
end
if relevant then
local cid = data[vi]
if active and replaceable_cids[cid] then
data[vi] = c_field
elseif not active and cid == c_field then
data[vi] = c_air
end
end
vi = vi + 1
end
end
end
vm:set_data(data)
vm:update_liquids()
vm:write_to_map()
end
local function set_forcefield_formspec(meta)
local formspec
if digilines_path then
formspec = "size[5,3.25]"..
"field[0.3,3;5,1;channel;"..S("Digiline Channel")..";${channel}]"
else
formspec = "size[5,2.25]"
end
formspec = formspec..
"field[0.3,0.5;2,1;range;"..S("Range")..";${range}]"
-- The names for these toggle buttons are explicit about which
-- state they'll switch to, so that multiple presses (arising
-- from the ambiguity between lag and a missed press) only make
-- the single change that the user expects.
if meta:get_int("shape") == 0 then
formspec = formspec.."button[3,0.2;2,1;shape1;"..S("Sphere").."]"
else
formspec = formspec.."button[3,0.2;2,1;shape0;"..S("Cube").."]"
end
if meta:get_int("mesecon_mode") == 0 then
formspec = formspec.."button[0,1;5,1;mesecon_mode_1;"..S("Ignoring Mesecon Signal").."]"
else
formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
end
if meta:get_int("enabled") == 0 then
formspec = formspec..
"button[0,1.75;5,1;enable;"..S("@1 Disabled", S("@1 Forcefield Emitter", S("HV"))).."]"
else
formspec = formspec..
"button[0,1.75;5,1;disable;"..S("@1 Enabled", S("@1 Forcefield Emitter", S("HV"))).."]"
end
meta:set_string("formspec", formspec)
end
local forcefield_receive_fields = function(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.chat_send_player(player_name, S("You are not allowed to edit this!"))
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
local range = nil
if fields.range then
range = tonumber(fields.range) or 0
-- Smallest field is 5. Anything less is asking for trouble.
-- Largest is 20. It is a matter of pratical node handling.
-- At the maximim range updating the forcefield takes about 0.2s
range = math.max(range, 5)
range = math.min(range, 20)
if range == meta:get_int("range") then range = nil end
end
if fields.shape0 or fields.shape1 or range then
update_forcefield(pos, meta, false)
end
if range then meta:set_int("range", range) end
if fields.channel then meta:set_string("channel", fields.channel) end
if fields.shape0 then meta:set_int("shape", 0) end
if fields.shape1 then meta:set_int("shape", 1) end
if fields.enable then meta:set_int("enabled", 1) end
if fields.disable then meta:set_int("enabled", 0) end
if fields.mesecon_mode_0 then meta:set_int("mesecon_mode", 0) end
if fields.mesecon_mode_1 then meta:set_int("mesecon_mode", 1) end
set_forcefield_formspec(meta)
end
local mesecons = {
effector = {
action_on = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 1)
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 0)
end
}
}
local digiline_def = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local msgt = type(msg)
if msgt == "string" then
local smsg = msg:lower()
msg = {}
if smsg == "get" then
msg.command = "get"
elseif smsg == "off" then
msg.command = "off"
elseif smsg == "on" then
msg.command = "on"
elseif smsg == "toggle" then
msg.command = "toggle"
elseif smsg:sub(1, 5) == "range" then
msg.command = "range"
msg.value = tonumber(smsg:sub(7))
elseif smsg:sub(1, 5) == "shape" then
msg.command = "shape"
msg.value = smsg:sub(7):lower()
msg.value = tonumber(msg.value) or msg.value
end
elseif msgt ~= "table" then
return
end
if msg.command == "get" then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
enabled = meta:get_int("enabled"),
range = meta:get_int("range"),
shape = meta:get_int("shape")
})
return
elseif msg.command == "off" then
meta:set_int("enabled", 0)
elseif msg.command == "on" then
meta:set_int("enabled", 1)
elseif msg.command == "toggle" then
local onn = meta:get_int("enabled")
onn = 1-onn -- Mirror onn with pivot 0.5, so switch between 1 and 0.
meta:set_int("enabled", onn)
elseif msg.command == "range" then
if type(msg.value) ~= "number" then
return
end
msg.value = math.max(msg.value, 5)
msg.value = math.min(msg.value, 20)
update_forcefield(pos, meta, false)
meta:set_int("range", msg.value)
elseif msg.command == "shape" then
local valuet = type(msg.value)
if valuet == "string" then
if msg.value == "sphere" then
msg.value = 0
elseif msg.value == "cube" then
msg.value = 1
end
elseif valuet ~= "number" then
return
end
if not msg.value then
return
end
update_forcefield(pos, meta, false)
meta:set_int("shape", msg.value)
else
return
end
set_forcefield_formspec(meta)
end
},
}
local function run(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("HV_EU_input")
local enabled = meta:get_int("enabled") ~= 0 and
(meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
local machine_name = S("@1 Forcefield Emitter", S("HV"))
local range = meta:get_int("range")
local power_requirement
if meta:get_int("shape") == 0 then
power_requirement = math.floor(4 * math.pi * range * range)
else
power_requirement = 24 * range * range
end
power_requirement = power_requirement * forcefield_power_drain
if not enabled then
if node.name == "technic:forcefield_emitter_on" then
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
meta:set_string("infotext", S("@1 Disabled", machine_name))
end
meta:set_int("HV_EU_demand", 0)
return
end
meta:set_int("HV_EU_demand", power_requirement)
if eu_input < power_requirement then
meta:set_string("infotext", S("@1 Unpowered", machine_name))
if node.name == "technic:forcefield_emitter_on" then
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
end
elseif eu_input >= power_requirement then
if node.name == "technic:forcefield_emitter_off" then
technic.swap_node(pos, "technic:forcefield_emitter_on")
meta:set_string("infotext", S("@1 Active", machine_name) .. "\n" ..
S("Demand: @1", technic.EU_string(power_requirement)))
end
update_forcefield(pos, meta, true)
end
end
minetest.register_node("technic:forcefield_emitter_off", {
description = S("@1 Forcefield Emitter", S("HV")),
tiles = {
"technic_forcefield_emitter_off.png",
"technic_machine_bottom.png"..cable_entry,
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png"
},
groups = {cracky = 1, technic_machine = 1, technic_hv = 1, pickaxey = 3},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
on_receive_fields = forcefield_receive_fields,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("HV_EU_input", 0)
meta:set_int("HV_EU_demand", 0)
meta:set_int("range", 10)
meta:set_int("enabled", 0)
meta:set_int("mesecon_mode", 0)
meta:set_int("mesecon_effect", 0)
if digilines_path then
meta:set_string("channel", "forcefield"..minetest.pos_to_string(pos))
end
meta:set_string("infotext", S("@1 Forcefield Emitter", S("HV")))
set_forcefield_formspec(meta)
end,
mesecons = mesecons,
digiline = digiline_def,
technic_run = run,
})
minetest.register_node("technic:forcefield_emitter_on", {
description = S("@1 Forcefield Emitter", S("HV")),
tiles = {
"technic_forcefield_emitter_on.png",
"technic_machine_bottom.png"..cable_entry,
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png"
},
groups = {cracky = 1, technic_machine = 1, technic_hv = 1,
not_in_creative_inventory=1, pickaxey = 3},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
drop = "technic:forcefield_emitter_off",
on_receive_fields = forcefield_receive_fields,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
update_forcefield(pos, meta, false)
end,
mesecons = mesecons,
digiline = digiline_def,
technic_run = run,
technic_on_disable = function (pos, node)
local meta = minetest.get_meta(pos)
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
end,
on_blast = function(pos, intensity)
minetest.dig_node(pos)
return {"technic:forcefield_emitter_off"}
end,
})
minetest.register_node("technic:forcefield", {
description = S("@1 Forcefield", S("HV")),
sunlight_propagates = true,
drawtype = "glasslike",
groups = {not_in_creative_inventory=1},
is_ground_content = false,
paramtype = "light",
light_source = minetest.LIGHT_MAX,
diggable = false,
drop = '',
tiles = {{
name = "technic_forcefield_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1.0,
},
}},
on_blast = function(pos, intensity)
end,
})
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper("technic:forcefield")
end
technic.register_machine("HV", "technic:forcefield_emitter_on", technic.receiver)
technic.register_machine("HV", "technic:forcefield_emitter_off", technic.receiver)

View file

@ -0,0 +1,13 @@
minetest.register_alias("hv_generator", "technic:hv_generator")
minetest.register_craft({
output = 'technic:hv_generator',
recipe = {
{'technic:carbon_plate', 'technic:mv_generator', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_generator({tier="HV", tube=1, supply=1200})

View file

@ -0,0 +1,21 @@
-- HV grinder
local S = technic.getter
minetest.register_craft({
output = 'technic:hv_grinder',
recipe = {
{'technic:carbon_plate', 'technic:mv_grinder', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:hv_grinder", {
typename = "grinding",
description = S("@1 Grinder", S("HV")),
tier="HV",
demand={1200, 900, 600},
speed=5,
upgrade=1,
tube=1
})

View file

@ -0,0 +1,20 @@
technic.register_tier("HV", "High Voltage")
local path = technic.modpath.."/machines/HV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
dofile(path.."/solar_array.lua")
dofile(path.."/nuclear_reactor.lua")
dofile(path.."/generator.lua")
-- Machines
dofile(path.."/quarry.lua")
dofile(path.."/forcefield.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/compressor.lua")

View file

@ -0,0 +1,557 @@
--[[
The enriched uranium rod driven EU generator.
A very large and advanced machine providing vast amounts of power.
Very efficient but also expensive to run as it needs uranium.
Provides 100000 HV EUs for one week (only counted when loaded).
The nuclear reactor core requires a casing of water and a protective
shield to work. This is checked now and then and if the casing is not
intact the reactor will melt down!
--]]
local burn_ticks = 7 * 24 * 60 * 60 -- Seconds
local power_supply = 100000 -- EUs
local fuel_type = "technic:uranium_fuel" -- The reactor burns this
local digiline_meltdown = technic.config:get_bool("enable_nuclear_reactor_digiline_selfdestruct")
local has_digilines = minetest.get_modpath("digilines")
local has_mcl = minetest.get_modpath("mcl_core")
local mat = technic.materials
local S = technic.getter
local reactor_desc = S("@1 Nuclear Reactor Core", S("HV"))
local cable_entry = "^technic_cable_connection_overlay.png"
-- FIXME: Recipe should make more sense like a rod recepticle, steam chamber, HV generator?
minetest.register_craft({
output = 'technic:hv_nuclear_reactor_core',
recipe = {
{'technic:carbon_plate', mat.obsidian_glass, 'technic:carbon_plate'},
{'technic:composite_plate', 'technic:machine_casing', 'technic:composite_plate'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
local size = minetest.get_modpath("mcl_formspec") and "size[9,9]" or "size[8,9]"
local function make_reactor_formspec(meta)
local f = size..
"label[0,0;"..S("Nuclear Reactor Rod Compartment").."]"..
"list[context;src;2,1;3,2;]"..
"listring[context;src]"..
"button[5.5,1.5;2,1;start;"..S("Start").."]"..
"checkbox[5.5,2.5;autostart;"..S("Automatic Start")..";"..meta:get_string("autostart").."]"
if has_mcl then
f = f..
mcl_formspec.get_itemslot_bg(2,1,3,2)..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
"listring[current_player;main]"
else
f = f..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"
end
if not has_digilines then
return f
end
local digiline_enabled = meta:get_string("enable_digiline")
f = f.."checkbox[0.5,2.8;enable_digiline;Enable Digiline;"..digiline_enabled.."]"
if digiline_enabled ~= "true" then
return f
end
return f..
"button_exit[4.6,3.69;2,1;save;"..S("Save").."]"..
"field[1,4;4,1;channel;"..S("Digiline Channel")..";${channel}]"
end
local SS_OFF = 0
local SS_DANGER = 1
local SS_CLEAR = 2
local reactor_siren = {}
local function siren_set_state(pos, state)
local hpos = minetest.hash_node_position(pos)
local siren = reactor_siren[hpos]
if not siren then
if state == SS_OFF then return end
siren = {state=SS_OFF}
reactor_siren[hpos] = siren
end
if state == SS_DANGER and siren.state ~= SS_DANGER then
if siren.handle then minetest.sound_stop(siren.handle) end
siren.handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_danger_loop",
{pos=pos, gain=1.5, loop=true, max_hear_distance=48})
siren.state = SS_DANGER
elseif state == SS_CLEAR then
if siren.handle then minetest.sound_stop(siren.handle) end
local clear_handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_clear",
{pos=pos, gain=1.5, loop=false, max_hear_distance=48})
siren.handle = clear_handle
siren.state = SS_CLEAR
minetest.after(10, function()
if siren.handle ~= clear_handle then return end
minetest.sound_stop(clear_handle)
if reactor_siren[hpos] == siren then
reactor_siren[hpos] = nil
end
end)
elseif state == SS_OFF and siren.state ~= SS_OFF then
if siren.handle then minetest.sound_stop(siren.handle) end
reactor_siren[hpos] = nil
end
end
local function siren_danger(pos, meta)
meta:set_int("siren", 1)
siren_set_state(pos, SS_DANGER)
end
local function siren_clear(pos, meta)
if meta:get_int("siren") ~= 0 then
siren_set_state(pos, SS_CLEAR)
meta:set_int("siren", 0)
end
end
--[[
The standard reactor structure consists of a 9x9x9 cube. A cross
section through the middle:
CCCC CCCC
CBBB BBBC
CBLL LLBC
CBLWWWLBC
CBLW#WLBC
CBLW|WLBC
CBLL|LLBC
CBBB|BBBC
CCCC|CCCC
C = Concrete, B = Blast-resistant concrete, L = Lead,
W = water node, # = reactor core, | = HV cable
The man-hole is optional (but necessary for refueling).
For the reactor to operate and not melt down, it insists on the inner
7x7x7 portion (from the core out to the blast-resistant concrete)
being intact. Intactness only depends on the number of nodes of the
right type in each layer. The water layer must have water in all but
at most one node; the steel and blast-resistant concrete layers must
have the right material in all but at most two nodes. The permitted
gaps are meant for the cable and man-hole, but can actually be anywhere
and contain anything. For the reactor to be useful, a cable must
connect to the core, but it can go in any direction.
The outer concrete layer of the standard structure is not required
for the reactor to operate. It is noted here because it used to
be mandatory, and for historical reasons (that it predates the
implementation of radiation) it needs to continue being adequate
shielding of legacy reactors. If it ever ceases to be adequate
shielding for new reactors, legacy ones should be grandfathered.
For legacy reasons, if the reactor has a stainless steel layer instead
of a lead layer it will be converted to a lead layer.
--]]
local function reactor_structure_badness(pos)
local vm = VoxelManip()
local pos1 = vector.subtract(pos, 3)
local pos2 = vector.add(pos, 3)
local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
local data = vm:get_data()
local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge})
local c_blast_concrete = minetest.get_content_id("technic:blast_resistant_concrete")
local c_lead = minetest.get_content_id("technic:lead_block")
local c_steel = minetest.get_content_id("technic:stainless_steel_block")
local c_water_source = minetest.get_content_id(mat.water_source)
local c_water_flowing = minetest.get_content_id(mat.water_flowing)
local blast_layer, steel_layer, lead_layer, water_layer = 0, 0, 0, 0
for z = pos1.z, pos2.z do
for y = pos1.y, pos2.y do
for x = pos1.x, pos2.x do
local cid = data[area:index(x, y, z)]
if x == pos1.x or x == pos2.x or
y == pos1.y or y == pos2.y or
z == pos1.z or z == pos2.z then
if cid == c_blast_concrete then
blast_layer = blast_layer + 1
end
elseif x == pos1.x+1 or x == pos2.x-1 or
y == pos1.y+1 or y == pos2.y-1 or
z == pos1.z+1 or z == pos2.z-1 then
if cid == c_lead then
lead_layer = lead_layer + 1
elseif cid == c_steel then
steel_layer = steel_layer + 1
end
elseif x == pos1.x+2 or x == pos2.x-2 or
y == pos1.y+2 or y == pos2.y-2 or
z == pos1.z+2 or z == pos2.z-2 then
if cid == c_water_source or cid == c_water_flowing then
water_layer = water_layer + 1
end
end
end
end
end
if steel_layer >= 96 then
for z = pos1.z+1, pos2.z-1 do
for y = pos1.y+1, pos2.y-1 do
for x = pos1.x+1, pos2.x-1 do
local vi = area:index(x, y, z)
if x == pos1.x+1 or x == pos2.x-1 or
y == pos1.y+1 or y == pos2.y-1 or
z == pos1.z+1 or z == pos2.z-1 then
if data[vi] == c_steel then
data[vi] = c_lead
end
end
end
end
end
vm:set_data(data)
vm:write_to_map()
lead_layer = steel_layer
end
if water_layer > 25 then water_layer = 25 end
if lead_layer > 96 then lead_layer = 96 end
if blast_layer > 216 then blast_layer = 216 end
return (25 - water_layer) + (96 - lead_layer) + (216 - blast_layer)
end
local mcl_expl_info = {
drop_chance = 1.0,
max_blast_resistance = 10,
sound = true,
particles = true,
fire = true,
griefing = true,
grief_protected = true,
}
local function melt_down_reactor(pos)
minetest.log("action", "A reactor melted down at "..minetest.pos_to_string(pos))
if minetest.get_modpath("mcl_explosions") then
mcl_explosions.explode(pos, 30, mcl_expl_info)
end
minetest.set_node(pos, {name = "technic:corium_source"})
end
local function start_reactor(pos, meta)
if minetest.get_node(pos).name ~= "technic:hv_nuclear_reactor_core" then
return false
end
local inv = meta:get_inventory()
if inv:is_empty("src") then
return false
end
local src_list = inv:get_list("src")
local correct_fuel_count = 0
for _, src_stack in pairs(src_list) do
if src_stack and src_stack:get_name() == fuel_type then
correct_fuel_count = correct_fuel_count + 1
end
end
-- Check that the reactor is complete and has the correct fuel
if correct_fuel_count ~= 6 or reactor_structure_badness(pos) ~= 0 then
return false
end
meta:set_int("burn_time", 1)
technic.swap_node(pos, "technic:hv_nuclear_reactor_core_active")
meta:set_int("HV_EU_supply", power_supply)
for idx, src_stack in pairs(src_list) do
src_stack:take_item()
inv:set_stack("src", idx, src_stack)
end
return true
end
minetest.register_abm({
label = "Machines: reactor melt-down check",
nodenames = {"technic:hv_nuclear_reactor_core_active"},
interval = 4,
chance = 1,
action = function (pos, node)
local meta = minetest.get_meta(pos)
local badness = reactor_structure_badness(pos)
local accum_badness = meta:get_int("structure_accumulated_badness")
if badness == 0 then
if accum_badness ~= 0 then
meta:set_int("structure_accumulated_badness", math.max(accum_badness - 4, 0))
siren_clear(pos, meta)
end
else
siren_danger(pos, meta)
accum_badness = accum_badness + badness
if accum_badness >= 25 then
melt_down_reactor(pos)
else
meta:set_int("structure_accumulated_badness", accum_badness)
end
end
end,
})
local function run(pos, node)
local meta = minetest.get_meta(pos)
local burn_time = meta:get_int("burn_time") or 0
if burn_time >= burn_ticks or burn_time == 0 then
if has_digilines and meta:get_int("HV_EU_supply") == power_supply then
digilines.receptor_send(pos, technic.digilines.rules_allfaces,
-- TODO: Remove "remote_channel" and use de facto standard "channel"
meta:get("channel") or meta:get_string("remote_channel"),
{
command = "fuel_used",
pos = pos
}
)
end
if meta:get_string("autostart") == "true" then
if start_reactor(pos, meta) then
return
end
end
meta:set_int("HV_EU_supply", 0)
meta:set_int("burn_time", 0)
meta:set_string("infotext", S("@1 Idle", reactor_desc))
technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
meta:set_int("structure_accumulated_badness", 0)
siren_clear(pos, meta)
elseif burn_time > 0 then
burn_time = burn_time + 1
meta:set_int("burn_time", burn_time)
local percent = math.floor(burn_time / burn_ticks * 100)
meta:set_string("infotext", S("@1 (@2% Fuel Used)", reactor_desc, percent))
meta:set_int("HV_EU_supply", power_supply)
end
end
local nuclear_reactor_receive_fields = function(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.chat_send_player(player_name, S("You are not allowed to edit this!"))
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
local update_formspec = false
if fields.channel or fields.remote_channel then
-- TODO: Remove "remote_channel" and use de facto standard "channel"
meta:set_string("remote_channel", fields.channel or fields.remote_channel)
meta:set_string("channel", fields.channel or fields.remote_channel)
end
if fields.start then
local b = start_reactor(pos, meta)
if b then
minetest.chat_send_player(player_name, S("Start successful"))
else
minetest.chat_send_player(player_name, S("Error"))
end
end
if fields.autostart then
meta:set_string("autostart", fields.autostart)
update_formspec = true
end
if fields.enable_digiline then
meta:set_string("enable_digiline", fields.enable_digiline)
update_formspec = true
end
if update_formspec then
meta:set_string("formspec", make_reactor_formspec(meta))
end
end
local digiline_def = function(pos, _, channel, msg)
local meta = minetest.get_meta(pos)
if meta:get_string("enable_digiline") ~= "true" or
-- TODO: Remove "remote_channel" and use de facto standard "channel"
channel ~= (meta:get("channel") or meta:get_string("remote_channel")) then
return
end
-- Convert string messages to tables:
local msgt = type(msg)
if msgt == "string" then
local smsg = msg:lower()
msg = {}
if smsg == "get" then
msg.command = "get"
elseif smsg:sub(1, 13) == "self_destruct" then
msg.command = "self_destruct"
msg.timer = tonumber(smsg:sub(15)) or 0
elseif smsg == "start" then
msg.command = "start"
end
elseif msgt ~= "table" then
return
end
if msg.command == "get" then
local inv = meta:get_inventory()
local invtable = {}
for i = 1, 6 do
local stack = inv:get_stack("src", i)
if stack:is_empty() then
invtable[i] = 0
elseif stack:get_name() == fuel_type then
invtable[i] = stack:get_count()
else
invtable[i] = -stack:get_count()
end
end
digilines.receptor_send(pos, technic.digilines.rules_allfaces, channel, {
burn_time = meta:get_int("burn_time"),
enabled = meta:get_int("HV_EU_supply") == power_supply,
siren = meta:get_int("siren") == 1,
structure_accumulated_badness = meta:get_int("structure_accumulated_badness"),
rods = invtable
})
elseif digiline_meltdown and msg.command == "self_destruct" and
minetest.get_node(pos).name == "technic:hv_nuclear_reactor_core_active" then
if msg.timer ~= 0 and type(msg.timer) == "number" then
siren_danger(pos, meta)
minetest.after(msg.timer, melt_down_reactor, pos)
else
melt_down_reactor(pos)
end
elseif msg.command == "start" then
local b = start_reactor(pos, meta)
if b then
digilines.receptor_send(pos, technic.digilines.rules_allfaces, channel, {
command = "start_success",
pos = pos
})
else
digilines.receptor_send(pos, technic.digilines.rules_allfaces, channel, {
command = "start_error",
pos = pos
})
end
end
end
minetest.register_node("technic:hv_nuclear_reactor_core", {
description = reactor_desc,
tiles = {
"technic_hv_nuclear_reactor_core.png",
"technic_hv_nuclear_reactor_core.png"..cable_entry
},
drawtype = "mesh",
mesh = "technic_reactor.obj",
groups = {cracky = 1, technic_machine = 1, technic_hv = 1, pickaxey = 3},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
paramtype = "light",
paramtype2 = "facedir",
stack_max = 1,
on_receive_fields = nuclear_reactor_receive_fields,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", reactor_desc)
meta:set_string("formspec", make_reactor_formspec(meta))
local inv = meta:get_inventory()
inv:set_size("src", 6)
end,
-- digiline interface
digiline = {
receptor = {
rules = technic.digilines.rules_allfaces,
action = function() end,
},
effector = {
rules = technic.digilines.rules_allfaces,
action = digiline_def,
},
},
can_dig = technic.machine_can_dig,
on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
technic_run = run,
})
minetest.register_node("technic:hv_nuclear_reactor_core_active", {
tiles = {
"technic_hv_nuclear_reactor_core.png",
"technic_hv_nuclear_reactor_core.png"..cable_entry
},
drawtype = "mesh",
mesh = "technic_reactor.obj",
groups = {cracky = 1, technic_machine = 1, technic_hv = 1, radioactive = 4,
not_in_creative_inventory = 1, pickaxey = 3},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
drop = "technic:hv_nuclear_reactor_core",
light_source = 14,
paramtype = "light",
paramtype2 = "facedir",
on_receive_fields = nuclear_reactor_receive_fields,
-- digiline interface
digiline = {
receptor = {
rules = technic.digilines.rules_allfaces,
action = function() end,
},
effector = {
rules = technic.digilines.rules_allfaces,
action = digiline_def,
},
},
can_dig = technic.machine_can_dig,
after_dig_node = melt_down_reactor,
on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
technic_run = run,
technic_on_disable = function(pos, node)
local timer = minetest.get_node_timer(pos)
timer:start(1)
end,
on_timer = function(pos, node)
-- Connected back?
if technic.get_timeout("HV", pos) > 0 then return false end
local meta = minetest.get_meta(pos)
local burn_time = meta:get_int("burn_time") or 0
if burn_time >= burn_ticks or burn_time == 0 then
meta:set_int("HV_EU_supply", 0)
meta:set_int("burn_time", 0)
technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
meta:set_int("structure_accumulated_badness", 0)
siren_clear(pos, meta)
return false
end
meta:set_int("burn_time", burn_time + 1)
return true
end,
})
technic.register_machine("HV", "technic:hv_nuclear_reactor_core", technic.producer)
technic.register_machine("HV", "technic:hv_nuclear_reactor_core_active", technic.producer)

View file

@ -0,0 +1,640 @@
local S = technic.getter
local has_digilines = minetest.get_modpath("digilines")
local has_mesecons = minetest.get_modpath("mesecons")
local has_vizlib = minetest.get_modpath("vizlib")
local has_jumpdrive = minetest.get_modpath("jumpdrive")
local has_mcl = minetest.get_modpath("mcl_formspec")
local quarry_max_depth = technic.config:get_int("quarry_max_depth")
local quarry_dig_particles = technic.config:get_bool("quarry_dig_particles")
local quarry_time_limit = technic.config:get_int("quarry_time_limit")
local quarry_demand = 10000
local network_time_limit = 30000
local infotext
do
local name = S("@1 Quarry", S("HV"))
local demand = S("Demand: @1", technic.EU_string(quarry_demand))
infotext = {
active = S("@1 Active", name).."\n"..demand,
disabled = S("@1 Disabled", name),
finished = S("@1 Finished", name),
purge = S("@1 Purging Cache", name),
unpowered = S("@1 Unpowered", name),
}
end
-- Hard-coded outward-spiral dig pattern for up to 17x17 dig area
local dig_pattern = {
0,1,2,2,3,3,0,0,0,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,1,1,1,1,1,2,2,
2,2,2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
-- Convert the dig pattern values to x/z offset vectors
do
local head = vector.new()
dig_pattern[0] = head
for i = 1, #dig_pattern do
head = vector.add(head, minetest.facedir_to_dir(dig_pattern[i]))
dig_pattern[i] = {x = head.x, z = head.z}
end
end
-- Cache of pipeworks fake players
local fake_players = {}
minetest.register_on_leaveplayer(function(player)
fake_players[player:get_player_name()] = nil
end)
local function get_fake_player(name)
if not fake_players[name] then
fake_players[name] = pipeworks.create_fake_player({name = name})
end
return fake_players[name]
end
local function player_allowed(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name then
return true
end
return not minetest.is_protected(pos, name)
end
local function can_dig_node(pos, dig_pos, node_name, owner, digger)
if node_name == "air" or node_name == "vacuum:vacuum" then
return false
end
if vector.equals(pos, dig_pos) then
return false -- Don't dig self
end
local def = minetest.registered_nodes[node_name]
if not def or not def.diggable or (def.can_dig and not def.can_dig(dig_pos, digger)) then
return false
end
if def._mcl_hardness == -1 then
return false
end
return not minetest.is_protected(dig_pos, owner)
end
local function do_purge(pos, meta)
local inv = meta:get_inventory()
for i, stack in ipairs(inv:get_list("cache")) do
if not stack:is_empty() then
technic.tube_inject_item(pos, pos, vector.new(0, 1, 0), stack:to_table())
inv:set_stack("cache", i, "")
break
end
end
if inv:is_empty("cache") then
meta:set_int("purge_on", 0)
end
end
local function spawn_dig_particles(pos, dig_pos, node)
local end_pos = vector.new(pos.x, pos.y - 0.5, pos.z)
local dist = vector.distance(dig_pos, end_pos)
local t = math.sqrt((2 * dist) / 20)
local acc = vector.multiply(vector.subtract(end_pos, dig_pos), (1 / dist) * 20)
minetest.add_particlespawner({
amount = 50,
time = 0.5,
minpos = vector.subtract(dig_pos, 0.4),
maxpos = vector.add(dig_pos, 0.4),
minacc = acc,
maxacc = acc,
minsize = 0.5,
maxsize = 1.5,
minexptime = t,
maxexptime = t,
node = node,
})
end
local function do_digging(pos, meta, net_time)
local us_start = minetest.get_us_time()
local step = tonumber(meta:get("step") or "")
if not step then
-- Missing metadata or not yet updated by conversion LBM, abort digging
return
end
local radius = meta:get_int("size")
local diameter = radius * 2 + 1
local num_steps = diameter * diameter
local dug = meta:get_int("dug")
local max_depth = meta:get_int("max_depth")
local offset = {
x = meta:get_int("offset_x"),
y = math.floor(step / num_steps) + 1 - meta:get_int("offset_y"),
z = meta:get_int("offset_z")
}
if dug == -1 then
-- Find ground before digging
if offset.y > max_depth then
meta:set_int("finished", 1)
return
end
local pos1 = {
x = pos.x + offset.x - radius,
y = pos.y - offset.y,
z = pos.z + offset.z - radius
}
local pos2 = {
x = pos.x + offset.x + radius,
y = pos.y - offset.y,
z = pos.z + offset.z + radius
}
minetest.load_area(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"air", "vacuum:vacuum"})
if #nodes < num_steps then
-- There are nodes to dig, start digging at this layer
meta:set_int("dug", 0)
else
-- Move down to next layer
meta:set_int("step", step + num_steps)
end
return
end
local owner = meta:get_string("owner")
local digger = get_fake_player(owner)
while true do
-- Search for something to dig
if offset.y > max_depth then
-- Finished digging
meta:set_int("finished", 1)
meta:set_int("purge_on", 1)
break
end
local dig_offset = dig_pattern[step % num_steps]
local dig_pos = {
x = pos.x + offset.x + dig_offset.x,
y = pos.y - offset.y,
z = pos.z + offset.z + dig_offset.z,
}
step = step + 1
if step % num_steps == 0 then
-- Finished this layer, move down
offset.y = offset.y + 1
end
local node = technic.get_or_load_node(dig_pos)
if can_dig_node(pos, dig_pos, node.name, owner, digger) then
-- Found something to dig, dig it and stop
minetest.remove_node(dig_pos)
if quarry_dig_particles then
spawn_dig_particles(pos, dig_pos, node)
end
local inv = meta:get_inventory()
local drops = minetest.get_node_drops(node.name, "")
local full = false
for _, item in ipairs(drops) do
local left = inv:add_item("cache", item)
while not left:is_empty() do
-- Cache is full, forcibly purge until the item fits
full = true
do_purge(pos, meta)
left = inv:add_item("cache", left)
end
end
dug = dug + 1
if full or dug % 99 == 0 then
-- Time to purge the cache
meta:set_int("purge_on", 1)
end
break
end
local us_used = minetest.get_us_time() - us_start
if us_used > quarry_time_limit or net_time + us_used > network_time_limit then
break
end
end
meta:set_int("dug", dug)
meta:set_int("step", step)
end
local function quarry_run(pos, _, _, network)
local meta = minetest.get_meta(pos)
if meta:get_int("purge_on") == 1 then
-- Purging
meta:set_string("infotext", infotext.purge)
meta:set_int("HV_EU_demand", 0)
do_purge(pos, meta)
elseif meta:get_int("finished") == 1 then
-- Finished
meta:set_string("infotext", infotext.finished)
meta:set_int("HV_EU_demand", 0)
elseif meta:get_int("enabled") == 1 then
-- Active
if meta:get_int("HV_EU_input") >= quarry_demand then
meta:set_string("infotext", infotext.active)
do_digging(pos, meta, network.lag)
else
meta:set_string("infotext", infotext.unpowered)
end
meta:set_int("HV_EU_demand", quarry_demand)
else
-- Disabled
meta:set_int("HV_EU_demand", 0)
meta:set_string("infotext", infotext.disabled)
if not meta:get_inventory():is_empty("cache") then
meta:set_int("purge_on", 1)
end
end
end
local function reset_quarry(meta)
meta:set_int("step", 0)
meta:set_int("dug", -1)
meta:set_int("purge_on", 1)
meta:set_int("finished", 0)
end
local size = minetest.get_modpath("mcl_formspec") and "size[9,10]" or "size[8,9]"
local base_formspec = size..
"label[0,0;"..S("@1 Quarry", S("HV")).."]"..
"list[context;cache;0,0.7;4,3;]"..
"listring[context;cache]"..
"button[6,0.6;2,1;restart;"..S("Restart").."]"..
"field[4.3,2.1;2,1;size;"..S("Radius")..";${size}]"..
"field[6.3,2.1;2,1;max_depth;"..S("Max Depth")..";${max_depth}]"..
"field[4.3,3.1;1.333,1;offset_x;"..S("Offset X")..";${offset_x}]"..
"field[5.633,3.1;1.333,1;offset_y;"..S("Offset Y")..";${offset_y}]"..
"field[6.966,3.1;1.333,1;offset_z;"..S("Offset Z")..";${offset_z}]"
if has_digilines then
base_formspec = base_formspec..
"field[4.3,4.2;4,1;channel;"..S("Digiline Channel")..";${channel}]"
end
if has_mcl then
base_formspec = base_formspec..
mcl_formspec.get_itemslot_bg(0,0.7,4,3)..
-- player inventory
"list[current_player;main;0,5.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5.5,9,3)..
"list[current_player;main;0,8.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.74,9,1)..
"listring[current_player;main]"
else
base_formspec = base_formspec..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"
end
local function update_formspec(meta)
local fs = base_formspec
local status = S("Digging not started")
if meta:get_int("purge_on") == 1 then
status = S("Purging cache")
elseif meta:get_int("finished") == 1 then
status = S("Digging finished")
elseif meta:get_int("enabled") == 1 then
local diameter = meta:get_int("size") * 2 + 1
local num_steps = diameter * diameter
local y_level = math.floor(meta:get_int("step") / num_steps) + 1 - meta:get_int("offset_y")
if y_level < 0 then
status = S("Digging @1 m above machine", math.abs(y_level))
else
status = S("Digging @1 m below machine", y_level)
end
end
if meta:get_int("enabled") == 1 then
fs = fs.."button[4,0.6;2,1;disable;"..S("Enabled").."]"
else
fs = fs.."button[4,0.6;2,1;enable;"..S("Disabled").."]"
end
if has_mesecons then
local selected = meta:get("mesecons") or "true"
if has_jumpdrive then
fs = fs.."checkbox[0,3.6;mesecons;"..S("Enable Mesecons Control")..";"..selected.."]"
else
fs = fs.."checkbox[0,3.8;mesecons;"..S("Enable Mesecons Control")..";"..selected.."]"
end
end
if has_jumpdrive then
local selected = meta:get("reset_on_move") or "true"
if has_mesecons then
fs = fs.."checkbox[0,4.1;reset_on_move;"..S("Restart When Moved")..";"..selected.."]"
else
fs = fs.."checkbox[0,3.8;reset_on_move;"..S("Restart When Moved")..";"..selected.."]"
end
end
meta:set_string("formspec", fs.."label[4,0;"..status.."]")
end
local function clamp(value, min, max, default)
value = tonumber(value) or default or max
return math.min(math.max(value, min), max)
end
local function quarry_receive_fields(pos, _, fields, sender)
local player_name = sender:get_player_name()
if not player_allowed(pos, player_name) then
minetest.chat_send_player(player_name, S("You are not allowed to edit this!"))
return
end
local meta = minetest.get_meta(pos)
if fields.size then
meta:set_int("size", clamp(fields.size, 0, 8, 4))
end
if fields.max_depth then
local depth = clamp(fields.max_depth, 1, quarry_max_depth)
meta:set_int("max_depth", depth)
local ymin = -math.min(10, depth - 1)
meta:set_int("offset_y", clamp(meta:get_int("offset_y"), ymin, 10, 0))
end
if fields.offset_x then
meta:set_int("offset_x", clamp(fields.offset_x, -10, 10, 0))
end
if fields.offset_y then
local ymin = -math.min(10, meta:get_int("max_depth") - 1)
meta:set_int("offset_y", clamp(fields.offset_y, ymin, 10, 0))
end
if fields.offset_z then
meta:set_int("offset_z", clamp(fields.offset_z, -10, 10, 0))
end
if fields.mesecons then
meta:set_string("mesecons", fields.mesecons)
end
if fields.reset_on_move then
meta:set_string("reset_on_move", fields.reset_on_move)
end
if fields.channel then
meta:set_string("channel", fields.channel)
end
if fields.enable then meta:set_int("enabled", 1) end
if fields.disable then meta:set_int("enabled", 0) end
if fields.restart then reset_quarry(meta) end
update_formspec(meta)
end
local function show_working_area(pos, _, player)
if not player or player:get_wielded_item():get_name() ~= "" then
-- Only spawn particles when using an empty hand
return
end
local meta = minetest.get_meta(pos)
local radius = meta:get_int("size") + 0.5
local offset = vector.new(meta:get_int("offset_x"), meta:get_int("offset_y"), meta:get_int("offset_z"))
local depth = meta:get_int("max_depth") + 0.5
-- Draw area from top corner to bottom corner
local pos1 = vector.add(pos, vector.new(offset.x - radius, offset.y - 0.5, offset.z - radius))
local pos2 = vector.add(pos, vector.new(offset.x + radius, -depth, offset.z + radius))
vizlib.draw_area(pos1, pos2, {player = player})
end
local function digiline_action(pos, _, channel, msg)
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
-- Convert string message to table
if type(msg) == "string" then
msg = msg:lower()
if msg == "get" or msg == "on" or msg == "off" or msg == "restart" then
msg = {command = msg}
elseif msg:sub(1, 7) == "radius " then
msg = {command = "radius", value = msg:sub(8,-1)}
elseif msg:sub(1,10) == "max_depth " then
msg = {command = "max_depth", value = msg:sub(11,-1)}
elseif msg:sub(1,9) == "offset_x " then
msg = {command = "offset_x", value = msg:sub(10,-1)}
elseif msg:sub(1,9) == "offset_y " then
msg = {command = "offset_y", value = msg:sub(10,-1)}
elseif msg:sub(1,9) == "offset_z " then
msg = {command = "offset_z", value = msg:sub(10,-1)}
elseif msg:sub(1,7) == "offset " then
local s = string.split(msg:sub(8,-1), ",")
msg = {command = "offset", value = {x = s[1], y = s[2], z = s[3]}}
end
end
if type(msg) ~= "table" then return end
-- Convert old message format to new format
if msg.command ~= "set" and msg.command ~= "get" then
local cmd = msg.command
if cmd == "restart" then
msg = {command = "set", restart = true}
elseif cmd == "on" or cmd == "off" then
msg = {command = "set", enabled = msg.command == "on"}
elseif cmd == "radius" or cmd == "max_depth" or cmd == "offset"
or cmd == "offset_x" or cmd == "offset_y" or cmd == "offset_z" then
msg = {command = "set", [cmd] = msg.value}
end
end
-- Process message
if msg.command == "get" then
local diameter = meta:get_int("size") * 2 + 1
local num_steps = diameter * diameter
local offset = {
x = meta:get_int("offset_x"),
y = meta:get_int("offset_y"),
z = meta:get_int("offset_z")
}
digilines.receptor_send(pos, technic.digilines.rules, channel, {
enabled = meta:get_int("enabled") == 1,
finished = meta:get_int("finished") == 1,
radius = meta:get_int("size"),
max_depth = meta:get_int("max_depth"),
offset_x = offset.x,
offset_y = offset.y,
offset_z = offset.z,
offset = offset,
dug_nodes = meta:get_int("dug"),
dig_level = -(math.floor(meta:get_int("step") / num_steps) + 1 - offset.y),
})
elseif msg.command == "set" then
if msg.enabled ~= nil then
meta:set_int("enabled", msg.enabled == true and 1 or 0)
end
if msg.restart == true then
reset_quarry(meta)
end
if msg.radius then
meta:set_int("size", clamp(msg.radius, 0, 8, 4))
end
if msg.max_depth then
local depth = clamp(msg.max_depth, 1, quarry_max_depth)
meta:set_int("max_depth", depth)
local ymin = -math.min(10, depth - 1)
meta:set_int("offset_y", clamp(meta:get_int("offset_y"), ymin, 10, 0))
end
if msg.offset_x then
meta:set_int("offset_x", clamp(msg.offset_x, -10, 10, 0))
end
if msg.offset_y then
local ymin = -math.min(10, meta:get_int("max_depth") - 1)
meta:set_int("offset_y", clamp(msg.offset_y, ymin, 10, 0))
end
if msg.offset_z then
meta:set_int("offset_z", clamp(msg.offset_z, -10, 10, 0))
end
if msg.offset and type(msg.offset) == "table" then
local ymin = -math.min(10, meta:get_int("max_depth") - 1)
meta:set_int("offset_x", clamp(msg.offset.x, -10, 10, 0))
meta:set_int("offset_y", clamp(msg.offset.y, ymin, 10, 0))
meta:set_int("offset_z", clamp(msg.offset.z, -10, 10, 0))
end
end
end
minetest.register_node("technic:quarry", {
description = S("@1 Quarry", S("HV")),
tiles = {
"technic_carbon_steel_block.png^pipeworks_tube_connection_metallic.png",
"technic_carbon_steel_block.png^technic_quarry_bottom.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png"
},
groups = {cracky = 2, tubedevice = 1, technic_machine = 1, technic_hv = 1, pickaxey = 2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"front", "back", "left", "right"},
tube = {
connect_sides = {top = 1},
-- Lower priority than tubes, so items will prefer any tube to another quarry
priority = 10,
can_go = function(pos, node, velocity, stack)
-- Always eject up, even if items came in another way
return { vector.new(0, 1, 0) }
end
},
on_punch = has_vizlib and show_working_area or nil,
on_rightclick = function(pos)
local meta = minetest.get_meta(pos)
update_formspec(meta)
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("size", 4)
meta:set_int("offset_x", 0)
meta:set_int("offset_y", 0)
meta:set_int("offset_z", 0)
meta:set_int("max_depth", quarry_max_depth)
meta:set_string("mesecons", "false")
meta:set_string("reset_on_move", "false")
meta:get_inventory():set_size("cache", 12)
reset_quarry(meta)
update_formspec(meta)
end,
on_movenode = has_jumpdrive and function(_, pos)
local meta = minetest.get_meta(pos)
if meta:get("reset_on_move") ~= "false" then
reset_quarry(meta)
end
end or nil,
after_place_node = function(pos, placer, itemstack)
minetest.get_meta(pos):set_string("owner", placer:get_player_name())
pipeworks.scan_for_tube_objects(pos)
end,
can_dig = function(pos, player)
return minetest.get_meta(pos):get_inventory():is_empty("cache")
end,
after_dig_node = pipeworks.scan_for_tube_objects,
on_receive_fields = quarry_receive_fields,
technic_run = quarry_run,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return player_allowed(pos, player:get_player_name()) and count or 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
mesecons = {
effector = {
action_on = function(pos)
local meta = minetest.get_meta(pos)
if meta:get("mesecons") ~= "false" then
meta:set_int("enabled", 1)
end
end,
action_off = function(pos)
local meta = minetest.get_meta(pos)
if meta:get("mesecons") ~= "false" then
meta:set_int("enabled", 0)
end
end
}
},
digiline = {
receptor = {
rules = technic.digilines.rules,
},
effector = {
rules = technic.digilines.rules,
action = digiline_action,
}
},
})
minetest.register_craft({
output = "technic:quarry",
recipe = {
{"technic:carbon_plate", "pipeworks:filter", "technic:composite_plate"},
{"basic_materials:motor", "technic:machine_casing", "technic:diamond_drill_head"},
{"technic:carbon_steel_block", "technic:hv_cable", "technic:carbon_steel_block"}
}
})
technic.register_machine("HV", "technic:quarry", technic.receiver)
minetest.register_lbm({
label = "Old quarry conversion",
name = "technic:old_quarry_conversion",
nodenames = {"technic:quarry"},
run_at_every_load = false,
action = function(pos, node)
local meta = minetest.get_meta(pos)
if meta:get("step") then
-- Quarry v3, don't do anything
-- This can happen when a quarry is moved with a jumpdrive
return
elseif meta:get("quarry_pos") then
-- Quarry v2, calculate step if quarry is digging below
local level = meta:get_int("dig_level") - pos.y
if level < 0 then
local diameter = meta:get_int("size") * 2 + 1
local num_steps = diameter * diameter
-- Convert the negative value to positive, and go back up one level
level = math.abs(level + 1)
meta:set_int("step", level * num_steps)
end
-- Delete unused meta values
meta:set_string("quarry_dir", "")
meta:set_string("quarry_pos", "")
meta:set_string("dig_pos", "")
meta:set_string("dig_level", "")
meta:set_string("dig_index", "")
meta:set_string("dig_steps", "")
else
-- Quarry v1, reset quarry
reset_quarry(meta)
end
local dir = minetest.facedir_to_dir(node.param2)
local offset = vector.multiply(dir, meta:get_int("size") + 1)
meta:set_int("offset_x", offset.x)
meta:set_int("offset_y", 4)
meta:set_int("offset_z", offset.z)
if not meta:get("max_depth") then
meta:set_int("max_depth", quarry_max_depth)
end
update_formspec(meta)
end
})

View file

@ -0,0 +1,18 @@
-- The high voltage solar array is an assembly of medium voltage arrays.
-- Solar arrays are not able to store large amounts of energy.
minetest.register_craft({
output = 'technic:hv_solar_array 1',
recipe = {
{'technic:mv_solar_array', 'technic:mv_solar_array', 'technic:mv_solar_array'},
{'technic:carbon_plate', 'technic:hv_transformer', 'technic:composite_plate'},
{'', 'technic:hv_cable', ''},
}
})
technic.register_solar_array("technic:hv_solar_array", {
tier="HV",
power=100
})
minetest.register_alias("technic:solar_array_hv", "technic:hv_solar_array")

View file

@ -0,0 +1,23 @@
-- LV Alloy furnace
local S = technic.getter
local mat = technic.materials
-- FIXME: kpoppel: I'd like to introduce an induction heating element here...
minetest.register_craft({
output = 'technic:lv_alloy_furnace',
recipe = {
{mat.brick, mat.brick, mat.brick},
{mat.brick, 'technic:machine_casing', mat.brick},
{mat.brick, 'technic:lv_cable', mat.brick},
}
})
technic.register_base_machine("technic:lv_alloy_furnace", {
typename = "alloy",
description = S("@1 Alloy Furnace", S("LV")),
insert_object = technic.insert_object_unique_stack,
can_insert = technic.can_insert_unique_stack,
tier = "LV",
speed = 1,
demand = {300}
})

View file

@ -0,0 +1,19 @@
-- LV Battery box
minetest.register_craft({
output = 'technic:lv_battery_box0',
recipe = {
{'group:wood', 'group:wood', 'group:wood'},
{'technic:battery', 'technic:machine_casing', 'technic:battery'},
{'technic:battery', 'technic:lv_cable', 'technic:battery'},
}
})
technic.register_battery_box("technic:lv_battery_box", {
tier = "LV",
max_charge = 40000,
charge_rate = 1000,
discharge_rate = 4000,
charge_step = 500,
discharge_step = 800,
})

View file

@ -0,0 +1,57 @@
local S = technic.getter
local mat = technic.materials
minetest.register_alias("lv_cable", "technic:lv_cable")
minetest.register_craft({
output = 'technic:lv_cable 6',
recipe = {
{mat.paper, mat.paper, mat.paper},
{mat.copper_ingot, mat.copper_ingot, mat.copper_ingot},
{mat.paper, mat.paper, mat.paper},
}
})
minetest.register_craft({
output = "technic:lv_cable_plate_1 5",
recipe = {
{"" , "" , "technic:lv_cable"},
{"technic:lv_cable", "technic:lv_cable", "technic:lv_cable"},
{"" , "" , "technic:lv_cable"},
}
})
minetest.register_craft({
output = "technic:lv_cable",
recipe = {{"technic:lv_cable_plate_1"}}
})
-- Register cables
technic.register_cable("technic:lv_cable", {
tier = "LV",
size = 2/16,
description = S("@1 Cable", S("LV"))
})
technic.register_cable_plate("technic:lv_cable_plate", {
tier = "LV",
size = 2/16,
description = S("@1 Cable Plate", S("LV")),
tiles = {"technic_lv_cable.png"},
})
if minetest.get_modpath("digilines") then
technic.register_cable("technic:lv_digi_cable", {
tier = "LV",
size = 2/16,
description = S("@1 Digiline Cable", S("LV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } }
})
technic.register_cable_plate("technic:lv_digi_cable_plate", {
tier = "LV",
size = 2/16,
description = S("@1 Digiline Cable Plate", S("LV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } },
tiles = {"technic_lv_digi_cable.png"}
})
end

View file

@ -0,0 +1,26 @@
local S = technic.getter
local mat = technic.materials
minetest.register_alias("compressor", "technic:lv_compressor")
minetest.register_craft({
output = 'technic:lv_compressor',
recipe = {
{mat.stone, 'basic_materials:motor', mat.stone},
{'mesecons:piston', 'technic:machine_casing', 'mesecons:piston'},
{'basic_materials:silver_wire', 'technic:lv_cable', 'basic_materials:silver_wire'},
},
replacements = {
{"basic_materials:silver_wire", "basic_materials:empty_spool"},
{"basic_materials:silver_wire", "basic_materials:empty_spool"}
},
})
technic.register_base_machine("technic:lv_compressor", {
typename = "compressing",
description = S("@1 Compressor", S("LV")),
tier = "LV",
demand = {300},
speed = 1
})

View file

@ -0,0 +1,22 @@
-- LV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
local S = technic.getter
local mat = technic.materials
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:lv_electric_furnace',
recipe = {
{mat.cobble, mat.cobble, mat.cobble},
{mat.cobble, 'technic:machine_casing', mat.cobble},
{mat.cobble, 'technic:lv_cable', mat.cobble},
}
})
technic.register_base_machine("technic:lv_electric_furnace", {
typename = "cooking",
description = S("@1 Furnace", S("LV")),
tier="LV",
demand={300},
speed = 2
})

View file

@ -0,0 +1,35 @@
local S = technic.getter
minetest.register_alias("extractor", "technic:lv_extractor")
if technic.config:get_bool("enable_tree_tap") then
minetest.register_craft({
output = 'technic:lv_extractor',
recipe = {
{'technic:treetap', 'basic_materials:motor', 'technic:treetap'},
{'technic:treetap', 'technic:machine_casing', 'technic:treetap'},
{'', 'technic:lv_cable', ''},
}
})
else
minetest.register_craft({
output = 'technic:lv_extractor',
recipe = {
{'basic_materials:motor', 'pipeworks:tube_1', 'basic_materials:motor'},
{'technic:carbon_steel_ingot', 'technic:machine_casing', 'technic:carbon_steel_ingot'},
{'', 'technic:lv_cable', ''},
}
})
end
technic.register_base_machine("technic:lv_extractor", {
typename = "extracting",
description = S("@1 Extractor", S("LV")),
tier = "LV",
demand = {300},
speed = 1
})

View file

@ -0,0 +1,20 @@
-- The electric generator.
-- A simple device to get started on the electric machines.
-- Inefficient and expensive in fuel (200EU per tick)
-- Also only allows for LV machinery to run.
local mat = technic.materials
minetest.register_alias("lv_generator", "technic:lv_generator")
minetest.register_craft({
output = 'technic:lv_generator',
recipe = {
{mat.stone, mat.furnace, mat.stone},
{mat.stone, 'technic:machine_casing', mat.stone},
{mat.stone, 'technic:lv_cable', mat.stone},
}
})
technic.register_generator({tier="LV", supply=200})

View file

@ -0,0 +1,124 @@
-- A geothermal EU generator
-- Using hot lava and water this device can create energy from steam
-- The machine is only producing LV EUs and can thus not drive more advanced equipment
-- The output is a little more than the coal burning generator (max 300EUs)
minetest.register_alias("geothermal", "technic:geothermal")
local S = technic.getter
local mat = technic.materials
minetest.register_craft({
output = 'technic:geothermal',
recipe = {
{'technic:granite', mat.diamond, 'technic:granite'},
{'basic_materials:copper_wire', 'technic:machine_casing', 'basic_materials:copper_wire'},
{'technic:granite', 'technic:lv_cable', 'technic:granite'},
},
replacements = {
{"basic_materials:copper_wire", "basic_materials:empty_spool"},
{"basic_materials:copper_wire", "basic_materials:empty_spool"}
},
})
minetest.register_craftitem("technic:geothermal", {
description = S("Geothermal @1 Generator", S("LV")),
})
local check_node_around = function(pos)
local node = minetest.get_node(pos)
if node.name == mat.water_source or node.name == mat.water_flowing then return 1 end
if node.name == mat.lava_source or node.name == mat.lava_flowing then return 2 end
return 0
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_nodes = 0
local lava_nodes = 0
local production_level = 0
local eu_supply = 0
-- Correct positioning is water on one side and lava on the other.
-- The two cannot be adjacent because the lava the turns into obsidian or rock.
-- To get to 100% production stack the water and lava one extra block down as well:
-- WGL (W=Water, L=Lava, G=the generator, |=an LV cable)
-- W|L
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x+1, y=pos.y-1, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y-1, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
{x=pos.x, y=pos.y-1, z=pos.z-1},
}
for _, p in pairs(positions) do
local check = check_node_around(p)
if check == 1 then water_nodes = water_nodes + 1 end
if check == 2 then lava_nodes = lava_nodes + 1 end
end
if water_nodes == 1 and lava_nodes == 1 then production_level = 25; eu_supply = 50 end
if water_nodes == 2 and lava_nodes == 1 then production_level = 50; eu_supply = 100 end
if water_nodes == 1 and lava_nodes == 2 then production_level = 75; eu_supply = 200 end
if water_nodes == 2 and lava_nodes == 2 then production_level = 100; eu_supply = 300 end
if production_level > 0 then
meta:set_int("LV_EU_supply", eu_supply)
end
meta:set_string("infotext", S("@1 (@2% Efficiency)",
S("Geothermal @1 Generator", S("LV")), production_level))
if production_level > 0 and minetest.get_node(pos).name == "technic:geothermal" then
technic.swap_node (pos, "technic:geothermal_active")
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:geothermal")
meta:set_int("LV_EU_supply", 0)
end
end
minetest.register_node("technic:geothermal", {
description = S("Geothermal @1 Generator", S("LV")),
tiles = {"technic_geothermal_top.png", "technic_machine_bottom.png", "technic_geothermal_side.png",
"technic_geothermal_side.png", "technic_geothermal_side.png", "technic_geothermal_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
paramtype2 = "facedir",
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Geothermal @1 Generator", S("LV")))
meta:set_int("LV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:geothermal_active", {
description = S("Geothermal @1 Generator", S("LV")),
tiles = {"technic_geothermal_top_active.png", "technic_machine_bottom.png", "technic_geothermal_side.png",
"technic_geothermal_side.png", "technic_geothermal_side.png", "technic_geothermal_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, not_in_creative_inventory=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
drop = "technic:geothermal",
technic_run = run,
})
technic.register_machine("LV", "technic:geothermal", technic.producer)
technic.register_machine("LV", "technic:geothermal_active", technic.producer)

View file

@ -0,0 +1,20 @@
local S = technic.getter
local mat = technic.materials
minetest.register_alias("grinder", "technic:lv_grinder")
minetest.register_craft({
output = 'technic:lv_grinder',
recipe = {
{mat.desert_stone, mat.diamond, mat.desert_stone},
{mat.desert_stone, 'technic:machine_casing', mat.desert_stone},
{'technic:granite', 'technic:lv_cable', 'technic:granite'},
}
})
technic.register_base_machine("technic:lv_grinder", {
typename = "grinding",
description = S("@1 Grinder", S("LV")),
tier="LV",
demand={200},
speed=1
})

View file

@ -0,0 +1,27 @@
technic.register_tier("LV", "Low Voltage")
local path = technic.modpath.."/machines/LV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
dofile(path.."/solar_panel.lua")
dofile(path.."/solar_array.lua")
dofile(path.."/geothermal.lua")
dofile(path.."/water_mill.lua")
dofile(path.."/generator.lua")
-- Machines
dofile(path.."/alloy_furnace.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/extractor.lua")
dofile(path.."/compressor.lua")
dofile(path.."/music_player.lua")
dofile(path.."/led.lua")
dofile(path.."/lamp.lua")

View file

@ -0,0 +1,204 @@
-- LV Lamp - a powerful light source.
-- Illuminates a 7x7x3(H) volume below itself with light bright as the sun.
local S = technic.getter
local mat = technic.materials
local demand = 50
local desc = S("@1 Lamp", S("LV"))
local active_desc = S("@1 Active", desc).."\n"..S("Demand: @1", technic.EU_string(demand))
local unpowered_desc = S("@1 Unpowered", desc)
local off_desc = S("@1 Off", desc)
-- Invisible light source node used for illumination
minetest.register_node("technic:dummy_light_source", {
description = S("Dummy light source node"),
inventory_image = "technic_dummy_light_source.png",
wield_image = "technic_dummy_light_source.png",
paramtype = "light",
drawtype = "airlike",
light_source = 14,
sunlight_propagates = true,
walkable = false,
buildable_to = true,
diggable = false,
pointable = false,
--drop = "", -- Intentionally allowed to drop itself
groups = {not_in_creative_inventory = 1}
})
local cid_light = minetest.get_content_id("technic:dummy_light_source")
local cid_air = minetest.CONTENT_AIR
local function illuminate(pos, active)
local pos1 = {x = pos.x - 3, y = pos.y - 3, z = pos.z - 3}
local pos2 = {x = pos.x + 3, y = pos.y - 1, z = pos.z + 3}
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(pos1, pos2)
local va = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
local node_data = vm:get_data()
local find_node = active and cid_air or cid_light
local set_node = active and cid_light or cid_air
local dirty = false
for i in va:iterp(pos1, pos2) do
if node_data[i] == find_node then
node_data[i] = set_node
dirty = true
end
end
if dirty then
vm:set_data(node_data)
vm:write_to_map()
end
end
local function set_random_timer(pos, mint, maxt)
local t = math.random(mint * 10, maxt * 10) * 0.1
minetest.get_node_timer(pos):start(t)
end
local function lamp_run(pos, node)
local meta = minetest.get_meta(pos)
if meta:get_int("LV_EU_demand") == 0 then
return -- Lamp is turned off
end
local eu_input = meta:get_int("LV_EU_input")
if node.name == "technic:lv_lamp_active" then
if eu_input < demand then
technic.swap_node(pos, "technic:lv_lamp")
meta:set_string("infotext", unpowered_desc)
set_random_timer(pos, 0.2, 1)
end
elseif node.name == "technic:lv_lamp" then
if eu_input >= demand then
technic.swap_node(pos, "technic:lv_lamp_active")
meta:set_string("infotext", active_desc)
set_random_timer(pos, 0.2, 2)
end
end
end
local function lamp_toggle(pos, node, player)
if not player or minetest.is_protected(pos, player:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
if meta:get_int("LV_EU_demand") == 0 then
meta:set_string("infotext", active_desc)
meta:set_int("LV_EU_demand", demand)
else
technic.swap_node(pos, "technic:lv_lamp")
meta:set_string("infotext", off_desc)
meta:set_int("LV_EU_demand", 0)
set_random_timer(pos, 0.2, 1)
end
end
minetest.register_node("technic:lv_lamp", {
description = desc,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
collision_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
selection_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
tiles = {
"technic_lv_lamp_top.png",
"technic_lv_lamp_bottom.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png"
},
groups = {cracky = 2, technic_machine = 1, technic_lv = 1, pickaxey = 2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"front", "back", "left", "right", "top"},
can_dig = technic.machine_can_dig,
technic_run = lamp_run,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", desc)
meta:set_int("LV_EU_demand", demand)
end,
on_destruct = illuminate,
on_rightclick = lamp_toggle,
on_timer = function(pos)
illuminate(pos, false)
-- Don't start the timer again, otherwise lights will fight each other
end,
})
minetest.register_node("technic:lv_lamp_active", {
description = active_desc,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.5, 0.5, 0.5, -0.5, -0.2, -0.5}
},
collision_box = {
type = "fixed",
fixed = {0.5, 0.5, 0.5, -0.5, -0.2, -0.5}
},
selection_box = {
type = "fixed",
fixed = {0.5, 0.5, 0.5, -0.5, -0.2, -0.5}
},
tiles = {
"technic_lv_lamp_top.png",
"technic_lv_lamp_bottom.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png",
"technic_lv_lamp_side.png"
},
paramtype = "light",
light_source = 14,
drop = "technic:lv_lamp",
groups = {cracky = 2, technic_machine = 1, technic_lv = 1, not_in_creative_inventory = 1, pickaxey = 2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"front", "back", "left", "right", "top"},
can_dig = technic.machine_can_dig,
technic_run = lamp_run,
technic_on_disable = function(pos)
technic.swap_node(pos, "technic:lv_lamp")
set_random_timer(pos, 0.2, 1)
end,
on_destruct = illuminate,
on_rightclick = lamp_toggle,
on_timer = function(pos, elapsed)
if elapsed < 60 then -- Don't check immediately after being unloaded
illuminate(pos, true)
end
set_random_timer(pos, 30, 60) -- Check every 30-60 seconds
end,
})
technic.register_machine("LV", "technic:lv_lamp", technic.receiver)
technic.register_machine("LV", "technic:lv_lamp_active", technic.receiver)
minetest.register_craft({
output = "technic:lv_lamp",
recipe = {
{mat.glass, mat.glass, mat.glass},
{"technic:lv_led", "technic:lv_led", "technic:lv_led"},
{"mesecons_materials:glue", "technic:lv_cable", "mesecons_materials:glue"},
}
})

View file

@ -0,0 +1,102 @@
-- LED - a weak light source.
-- Intended primarily as a core component for LED lamps.
local S = technic.getter
local demand = 5
local desc = S("@1 LED", S("LV"))
local active_desc = S("@1 Active", desc).."\n"..S("Demand: @1", technic.EU_string(demand))
local unpowered_desc = S("@1 Unpowered", desc)
local function led_run(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("LV_EU_input")
if eu_input < demand and node.name == "technic:lv_led_active" then
technic.swap_node(pos, "technic:lv_led")
meta:set_string("infotext", unpowered_desc)
elseif eu_input >= demand and node.name == "technic:lv_led" then
technic.swap_node(pos, "technic:lv_led_active")
meta:set_string("infotext", active_desc)
end
end
minetest.register_node("technic:lv_led", {
description = desc,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
collision_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
selection_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
tiles = {"technic_lv_led.png"},
inventory_image = "technic_lv_led_inv.png",
sunlight_propagates = true,
groups = {cracky = 2, technic_machine = 1, technic_lv = 1, pickaxey = 2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"front", "back", "left", "right", "top", "bottom"},
can_dig = technic.machine_can_dig,
technic_run = led_run,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", desc)
meta:set_int("LV_EU_demand", demand)
end,
})
minetest.register_node("technic:lv_led_active", {
description = active_desc,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
collision_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
selection_box = {
type = "fixed",
fixed = {0.2, 0.2, 0.2, -0.2, -0.2, -0.2}
},
tiles = {"technic_lv_led.png"},
inventory_image = "technic_lv_led_inv.png",
paramtype = "light",
light_source = 9,
drop = "technic:lv_led",
sunlight_propagates = true,
groups = {cracky = 2, technic_machine = 1, technic_lv = 1, not_in_creative_inventory = 1, pickaxey = 2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"front", "back", "left", "right", "top", "bottom"},
can_dig = technic.machine_can_dig,
technic_run = led_run,
technic_on_disable = function(pos)
technic.swap_node(pos, "technic:lv_led")
end,
})
technic.register_machine("LV", "technic:lv_led", technic.receiver)
technic.register_machine("LV", "technic:lv_led_active", technic.receiver)
minetest.register_craft({
output = "technic:lv_led 2",
recipe = {
{"", "basic_materials:plastic_sheet", ""},
{"basic_materials:plastic_sheet", "technic:doped_silicon_wafer", "basic_materials:plastic_sheet"},
{"", "basic_materials:silver_wire", ""},
},
replacements = {{"basic_materials:silver_wire", "basic_materials:empty_spool"}},
})

View file

@ -0,0 +1,134 @@
-- LV Music player.
-- The player can play music. But it is high ampage!
local S = technic.getter
local mat = technic.materials
minetest.register_alias("music_player", "technic:music_player")
minetest.register_craft({
output = 'technic:music_player',
recipe = {
{'technic:chromium_ingot', mat.diamond, 'technic:chromium_ingot'},
{mat.diamond, 'technic:machine_casing', mat.diamond},
{mat.mossycobble, 'technic:lv_cable', mat.mossycobble},
}
})
local music_handles = {}
local function play_track(pos, track)
return minetest.sound_play("technic_track"..tostring(track),
{pos = pos, gain = 1.0, loop = true, max_hear_distance = 72,})
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("LV_EU_input")
local machine_name = S("@1 Music Player", S("LV"))
local demand = 150
local current_track = meta:get_int("current_track")
local pos_hash = minetest.hash_node_position(pos)
local music_handle = music_handles[pos_hash]
-- Setup meta data if it does not exist.
if not eu_input then
meta:set_int("LV_EU_demand", demand)
meta:set_int("LV_EU_input", 0)
return
end
if meta:get_int("active") == 0 then
meta:set_string("infotext", S("@1 Idle", machine_name))
meta:set_int("LV_EU_demand", 0)
return
end
if eu_input < demand then
meta:set_string("infotext", S("@1 Unpowered", machine_name))
if music_handle then
minetest.sound_stop(music_handle)
music_handle = nil
end
elseif eu_input >= demand then
meta:set_string("infotext", S("@1 Active", machine_name) .. "\n" ..
S("Demand: @1", technic.EU_string(demand)))
if not music_handle then
music_handle = play_track(pos, current_track)
end
end
music_handles[pos_hash] = music_handle
meta:set_int("LV_EU_demand", demand)
end
local function stop_player(pos, node)
local pos_hash = minetest.hash_node_position(pos)
local music_handle = music_handles[pos_hash]
if music_handle then
minetest.sound_stop(music_handle)
music_handles[pos_hash] = nil
end
end
local function set_display(meta)
meta:set_string("formspec",
"size[4,4.5]"..
"item_image[0,0;1,1;technic:music_player]"..
"label[1,0;"..S("@1 Music Player", S("LV")).."]"..
"button[0,1;1,1;track1;1]"..
"button[1,1;1,1;track2;2]"..
"button[2,1;1,1;track3;3]"..
"button[0,2;1,1;track4;4]"..
"button[1,2;1,1;track5;5]"..
"button[2,2;1,1;track6;6]"..
"button[0,3;1,1;track7;7]"..
"button[1,3;1,1;track8;8]"..
"button[2,3;1,1;track9;9]"..
"button[3,1;1,1;stop;"..S("Stop").."]"..
"label[0,4;"..(meta:get_int("active") == 0 and S("Stopped") or
S("Current track: @1", meta:get_int("current_track"))).."]")
end
minetest.register_node("technic:music_player", {
description = S("@1 Music Player", S("LV")),
tiles = {"technic_music_player_top.png", "technic_machine_bottom.png", "technic_music_player_side.png",
"technic_music_player_side.png", "technic_music_player_side.png", "technic_music_player_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, axey = 2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom"},
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Music Player", S("LV")))
set_display(meta)
end,
on_receive_fields = function(pos, formanme, fields, sender)
local new_track = nil
if fields.stop then new_track = 0 end
if fields.track1 then new_track = 1 end
if fields.track2 then new_track = 2 end
if fields.track3 then new_track = 3 end
if fields.track4 then new_track = 4 end
if fields.track5 then new_track = 5 end
if fields.track6 then new_track = 6 end
if fields.track7 then new_track = 7 end
if fields.track8 then new_track = 8 end
if fields.track9 then new_track = 9 end
if new_track then
stop_player(pos)
local meta = minetest.get_meta(pos)
meta:set_int("active", new_track == 0 and 0 or 1)
meta:set_int("current_track", new_track)
set_display(meta)
end
end,
on_destruct = stop_player,
technic_run = run,
technic_on_disable = stop_player,
})
technic.register_machine("LV", "technic:music_player", technic.receiver)

View file

@ -0,0 +1,22 @@
-- The solar array is an assembly of panels into a powerful array
-- The assembly can deliver more energy than the individual panel because
-- of the transformer unit which converts the panel output variations into
-- a stable supply.
-- Solar arrays are not able to store large amounts of energy.
-- The LV arrays are used to make medium voltage arrays.
minetest.register_craft({
output = 'technic:lv_solar_array 1',
recipe = {
{'technic:solar_panel', 'technic:solar_panel', 'technic:solar_panel'},
{'technic:carbon_steel_ingot', 'technic:lv_transformer', 'technic:carbon_steel_ingot'},
{'', 'technic:lv_cable', ''},
}
})
technic.register_solar_array("technic:lv_solar_array", {
tier="LV",
power=10
})
minetest.register_alias("technic:solar_array_lv", "technic:lv_solar_array")

View file

@ -0,0 +1,74 @@
-- Solar panels are the building blocks of LV solar arrays
-- They can however also be used separately but with reduced efficiency due to the missing transformer.
-- Individual panels are less efficient than when the panels are combined into full arrays.
local S = technic.getter
minetest.register_craft({
output = 'technic:solar_panel',
recipe = {
{'technic:doped_silicon_wafer', 'technic:doped_silicon_wafer', 'technic:doped_silicon_wafer'},
{'basic_materials:silver_wire', 'technic:lv_cable', 'mesecons_materials:glue'},
},
replacements = { {"basic_materials:silver_wire", "basic_materials:empty_spool"}, },
})
local run = function(pos, node)
-- The action here is to make the solar panel prodice power
-- Power is dependent on the light level and the height above ground
-- There are many ways to cheat by using other light sources like lamps.
-- As there is no way to determine if light is sunlight that is just a shame.
-- To take care of some of it solar panels do not work outside daylight hours or if
-- built below 0m
local pos1 = {x=pos.x, y=pos.y+1, z=pos.z}
local machine_name = S("Small Solar @1 Generator", S("LV"))
local light = minetest.get_node_light(pos1, nil)
local time_of_day = minetest.get_timeofday()
local meta = minetest.get_meta(pos)
if light == nil then light = 0 end
-- turn on panel only during day time and if sufficient light
-- I know this is counter intuitive when cheating by using other light sources underground.
if light >= 12 and time_of_day >= 0.24 and time_of_day <= 0.76 and pos.y > -10 then
local charge_to_give = math.floor((light + pos1.y) * 3)
charge_to_give = math.max(charge_to_give, 0)
charge_to_give = math.min(charge_to_give, 200)
meta:set_string("infotext", S("@1 Active (@2)", machine_name,
technic.EU_string(charge_to_give)))
meta:set_int("LV_EU_supply", charge_to_give)
else
meta:set_string("infotext", S("@1 Idle", machine_name))
meta:set_int("LV_EU_supply", 0)
end
end
minetest.register_node("technic:solar_panel", {
tiles = {"technic_solar_panel_top.png", "technic_solar_panel_bottom.png", "technic_solar_panel_side.png",
"technic_solar_panel_side.png", "technic_solar_panel_side.png", "technic_solar_panel_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom"},
sounds = technic.sounds.node_sound_wood_defaults(),
description = S("Small Solar @1 Generator", S("LV")),
active = false,
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("LV_EU_supply", 0)
meta:set_string("infotext", S("Small Solar @1 Generator", S("LV")))
end,
technic_run = run,
})
technic.register_machine("LV", "technic:solar_panel", technic.producer)

View file

@ -0,0 +1,115 @@
-- A water mill produces LV EUs by exploiting flowing water across it
-- It is a LV EU supplier and fairly low yield (max 180EUs)
-- It is a little over half as good as the thermal generator.
local S = technic.getter
local mat = technic.materials
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_alias("water_mill", "technic:water_mill")
minetest.register_craft({
output = 'technic:water_mill',
recipe = {
{'technic:marble', mat.diamond, 'technic:marble'},
{'group:wood', 'technic:machine_casing', 'group:wood'},
{'technic:marble', 'technic:lv_cable', 'technic:marble'},
}
})
local function check_node_around_mill(pos)
local node = minetest.get_node(pos)
if node.name == mat.water_flowing or node.name == mat.river_water_flowing then
return node.param2 -- returns approx. water flow, if any
end
return false
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_flow = 0
local production_level
local eu_supply
local max_output = 4 * 45 -- keeping it around 180, little more than previous 150 :)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _, p in pairs(positions) do
local check = check_node_around_mill(p)
if check then
water_flow = water_flow + check
end
end
eu_supply = math.min(4 * water_flow, max_output)
production_level = math.floor(100 * eu_supply / max_output)
meta:set_int("LV_EU_supply", eu_supply)
meta:set_string("infotext", S("@1 (@2% Efficiency)",
S("Hydro @1 Generator", S("LV")), production_level))
if production_level > 0 and
minetest.get_node(pos).name == "technic:water_mill" then
technic.swap_node (pos, "technic:water_mill_active")
meta:set_int("LV_EU_supply", 0)
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:water_mill")
end
end
minetest.register_node("technic:water_mill", {
description = S("Hydro @1 Generator", S("LV")),
tiles = {
"technic_water_mill_top.png",
"technic_machine_bottom.png"..cable_entry,
"technic_water_mill_side.png",
"technic_water_mill_side.png",
"technic_water_mill_side.png",
"technic_water_mill_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Hydro @1 Generator", S("LV")))
meta:set_int("LV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:water_mill_active", {
description = S("Hydro @1 Generator", S("LV")),
tiles = {"technic_water_mill_top_active.png", "technic_machine_bottom.png",
"technic_water_mill_side.png", "technic_water_mill_side.png",
"technic_water_mill_side.png", "technic_water_mill_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, not_in_creative_inventory=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
drop = "technic:water_mill",
technic_run = run,
technic_disabled_machine_name = "technic:water_mill",
})
technic.register_machine("LV", "technic:water_mill", technic.producer)
technic.register_machine("LV", "technic:water_mill_active", technic.producer)

View file

@ -0,0 +1,23 @@
-- MV alloy furnace
local S = technic.getter
minetest.register_craft({
output = 'technic:mv_alloy_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_alloy_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_alloy_furnace", {
typename = "alloy",
description = S("@1 Alloy Furnace", S("MV")),
insert_object = technic.insert_object_unique_stack,
can_insert = technic.can_insert_unique_stack,
tier = "MV",
speed = 1.5,
upgrade = 1,
tube = 1,
demand = {3000, 2000, 1000}
})

View file

@ -0,0 +1,21 @@
-- MV Battery box
minetest.register_craft({
output = 'technic:mv_battery_box0',
recipe = {
{'technic:lv_battery_box0', 'technic:lv_battery_box0', 'technic:lv_battery_box0'},
{'technic:lv_battery_box0', 'technic:mv_transformer', 'technic:lv_battery_box0'},
{'', 'technic:mv_cable', ''},
}
})
technic.register_battery_box("technic:mv_battery_box", {
tier = "MV",
max_charge = 200000,
charge_rate = 20000,
discharge_rate = 80000,
charge_step = 2000,
discharge_step = 8000,
upgrade = 1,
tube = 1,
})

View file

@ -0,0 +1,56 @@
local S = technic.getter
minetest.register_alias("mv_cable", "technic:mv_cable")
minetest.register_craft({
output = 'technic:mv_cable 3',
recipe ={
{'technic:rubber', 'technic:rubber', 'technic:rubber'},
{'technic:lv_cable', 'technic:lv_cable', 'technic:lv_cable'},
{'technic:rubber', 'technic:rubber', 'technic:rubber'},
}
})
minetest.register_craft({
output = "technic:mv_cable_plate_1 5",
recipe = {
{"" , "" , "technic:mv_cable"},
{"technic:mv_cable", "technic:mv_cable", "technic:mv_cable"},
{"" , "" , "technic:mv_cable"},
}
})
minetest.register_craft({
output = "technic:mv_cable",
recipe = {{"technic:mv_cable_plate_1"}}
})
-- Register cables
technic.register_cable("technic:mv_cable", {
tier = "MV",
size = 2.5/16,
description = S("@1 Cable", S("MV"))
})
technic.register_cable_plate("technic:mv_cable_plate", {
tier = "MV",
size = 2.5/16,
description = S("@1 Cable Plate", S("MV")),
tiles = {"technic_mv_cable.png"},
})
if minetest.get_modpath("digilines") then
technic.register_cable("technic:mv_digi_cable", {
tier = "MV",
size = 2.5/16,
description = S("@1 Digiline Cable", S("MV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } }
})
technic.register_cable_plate("technic:mv_digi_cable_plate", {
tier = "MV",
size = 2.5/16,
description = S("@1 Digiline Cable Plate", S("MV")),
digiline = { wire = { rules = technic.digilines.rules_allfaces } },
tiles = {"technic_mv_digi_cable.png"}
})
end

View file

@ -0,0 +1,20 @@
local S = technic.getter
minetest.register_craft({
output = "technic:mv_centrifuge",
recipe = {
{"basic_materials:motor", "technic:copper_plate", "technic:diamond_drill_head"},
{"technic:copper_plate", "technic:machine_casing", "technic:copper_plate" },
{"pipeworks:one_way_tube", "technic:mv_cable", "pipeworks:mese_filter" },
}
})
technic.register_base_machine("technic:mv_centrifuge", {
typename = "separating",
description = S("@1 Centrifuge", S("MV")),
tier = "MV",
demand = { 8000, 7000, 6000 },
speed = 2,
upgrade = 1,
tube = 1,
})

View file

@ -0,0 +1,21 @@
-- MV compressor
local S = technic.getter
minetest.register_craft({
output = 'technic:mv_compressor',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_compressor', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_compressor", {
typename = "compressing",
description = S("@1 Compressor", S("MV")),
tier = "MV",
demand = {800, 600, 400},
speed = 2,
upgrade = 1,
tube = 1
})

View file

@ -0,0 +1,26 @@
-- MV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
-- In addition to this it can be upgraded with microcontrollers and batteries
-- This new version uses the batteries to lower the power consumption of the machine
-- Also in addition this furnace can be attached to the pipe system from the pipeworks mod.
local S = technic.getter
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:mv_electric_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_electric_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_electric_furnace", {
typename = "cooking",
description = S("@1 Furnace", S("MV")),
tier="MV",
upgrade=1,
tube=1,
demand={2000, 1000, 500},
speed=4
})

View file

@ -0,0 +1,21 @@
-- MV extractor
local S = technic.getter
minetest.register_craft({
output = 'technic:mv_extractor',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_extractor', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_extractor", {
typename = "extracting",
description = S("@1 Extractor", S("MV")),
tier = "MV",
demand = {800, 600, 400},
speed = 2,
upgrade = 1,
tube = 1
})

View file

@ -0,0 +1,21 @@
-- MV freezer
local S = technic.getter
minetest.register_craft({
output = 'technic:mv_freezer',
recipe = {
{'technic:stainless_steel_ingot', 'basic_materials:motor', 'technic:stainless_steel_ingot'},
{'pipeworks:pipe_1_empty', 'technic:mv_transformer', 'pipeworks:pipe_1_empty'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_freezer", {
typename = "freezing",
description = S("@1 Freezer", S("MV")),
tier = "MV",
demand = {800, 600, 400},
speed = 0.5,
upgrade = 1,
tube = 1
})

View file

@ -0,0 +1,11 @@
minetest.register_craft({
output = 'technic:mv_generator',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_generator', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_generator({tier="MV", tube=1, supply=600})

View file

@ -0,0 +1,21 @@
-- MV grinder
local S = technic.getter
minetest.register_craft({
output = 'technic:mv_grinder',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_grinder', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_base_machine("technic:mv_grinder", {
typename = "grinding",
description = S("@1 Grinder", S("MV")),
tier="MV",
demand={600, 450, 300},
speed=2,
upgrade=1,
tube=1
})

View file

@ -0,0 +1,111 @@
-- A Hydro Turbine produces MV EUs by exploiting flowing water across it
-- It is a MV EU supplier and fairly high yield (max 1800EUs)
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_alias("hydro_turbine", "technic:hydro_turbine")
minetest.register_craft({
output = 'technic:hydro_turbine',
recipe = {
{'technic:stainless_steel_ingot', 'technic:water_mill', 'technic:stainless_steel_ingot'},
{'technic:water_mill', 'technic:mv_transformer', 'technic:water_mill'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
local function get_water_flow(pos)
local node = minetest.get_node(pos)
if minetest.get_item_group(node.name, "water") == 3 then
return node.param2 -- returns approx. water flow, if any
end
return 0
end
---
-- 10 times better than LV hydro because of 2 extra water mills and 4 stainless steel, a transformer and whatnot ;P.
-- Man hydro turbines are tough and long lasting. So, give it some value :)
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_flow = 0
local production_level
local eu_supply
local max_output = 40 * 45 -- Generates 1800EU/s
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _, p in pairs(positions) do
water_flow = water_flow + get_water_flow(p)
end
eu_supply = math.min(40 * water_flow, max_output)
production_level = math.floor(100 * eu_supply / max_output)
meta:set_int("MV_EU_supply", eu_supply)
meta:set_string("infotext", S("@1 (@2% Efficiency)",
S("Hydro @1 Generator", S("MV")), production_level))
if production_level > 0 and
minetest.get_node(pos).name == "technic:hydro_turbine" then
technic.swap_node(pos, "technic:hydro_turbine_active")
meta:set_int("MV_EU_supply", 0)
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:hydro_turbine")
end
end
minetest.register_node("technic:hydro_turbine", {
description = S("Hydro @1 Generator", S("MV")),
tiles = {
"technic_hydro_turbine_top.png",
"technic_machine_bottom.png"..cable_entry,
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_mv=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Hydro @1 Generator", S("MV")))
meta:set_int("MV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:hydro_turbine_active", {
description = S("Hydro @1 Generator", S("MV")),
tiles = {"technic_hydro_turbine_top_active.png", "technic_machine_bottom.png",
"technic_hydro_turbine_side.png", "technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png", "technic_hydro_turbine_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, axey=2, handy=1,
technic_machine=1, technic_mv=1, not_in_creative_inventory=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
drop = "technic:hydro_turbine",
technic_run = run,
technic_disabled_machine_name = "technic:hydro_turbine",
})
technic.register_machine("MV", "technic:hydro_turbine", technic.producer)
technic.register_machine("MV", "technic:hydro_turbine_active", technic.producer)

View file

@ -0,0 +1,28 @@
technic.register_tier("MV", "Medium Voltage")
local path = technic.modpath.."/machines/MV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
if technic.config:get_bool("enable_wind_mill") then
dofile(path.."/wind_mill.lua")
end
dofile(path.."/generator.lua")
dofile(path.."/solar_array.lua")
dofile(path.."/hydro_turbine.lua")
-- Machines
dofile(path.."/alloy_furnace.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/extractor.lua")
dofile(path.."/compressor.lua")
dofile(path.."/centrifuge.lua")
dofile(path.."/tool_workshop.lua")
dofile(path.."/freezer.lua")

View file

@ -0,0 +1,18 @@
minetest.register_craft({
output = 'technic:mv_solar_array 1',
recipe = {
{'technic:lv_solar_array', 'technic:lv_solar_array', 'technic:lv_solar_array'},
{'technic:carbon_steel_ingot', 'technic:mv_transformer', 'technic:carbon_steel_ingot'},
{'', 'technic:mv_cable', ''},
}
})
technic.register_solar_array("technic:mv_solar_array", {
tier="MV",
power=30
})
-- compatibility alias for upgrading from old versions of technic
minetest.register_alias("technic:solar_panel_mv", "technic:mv_solar_array")
minetest.register_alias("technic:solar_array_mv", "technic:mv_solar_array")

View file

@ -0,0 +1,152 @@
-- Tool workshop
-- This machine repairs tools.
minetest.register_alias("tool_workshop", "technic:tool_workshop")
local S = technic.getter
local mat = technic.materials
local tube_entry = "^pipeworks_tube_connection_wooden.png"
minetest.register_craft({
output = 'technic:tool_workshop',
recipe = {
{'group:wood', mat.diamond, 'group:wood'},
{'mesecons_pistons:piston_sticky_off', 'technic:machine_casing', 'technic:carbon_cloth'},
{mat.obsidian, 'technic:mv_cable', mat.obsidian},
}
})
local workshop_demand = {5000, 3500, 2000}
local size = minetest.get_modpath("mcl_formspec") and "size[9,9;]" or "size[8,9;]"
local workshop_formspec =
size..
"list[context;src;3,1;1,1;]"..
"label[0,0;"..S("@1 Tool Workshop", S("MV")).."]"..
"list[context;upgrade1;1,3;1,1;]"..
"list[context;upgrade2;2,3;1,1;]"..
"label[1,4;"..S("Upgrade Slots").."]"
if minetest.get_modpath("mcl_formspec") then
workshop_formspec = workshop_formspec..
mcl_formspec.get_itemslot_bg(3,1,1,1)..
mcl_formspec.get_itemslot_bg(1,3,1,1)..
mcl_formspec.get_itemslot_bg(2,3,1,1)..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)
else
workshop_formspec = workshop_formspec..
"list[current_player;main;0,5;8,4;]"
end
-- listrings
workshop_formspec = workshop_formspec..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;upgrade1]"..
"listring[current_player;main]"..
"listring[context;upgrade2]"..
"listring[current_player;main]"
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local eu_input = meta:get_int("MV_EU_input")
local machine_name = S("@1 Tool Workshop", S("MV"))
-- Setup meta data if it does not exist.
if not eu_input then
meta:set_int("MV_EU_demand", workshop_demand[1])
meta:set_int("MV_EU_input", 0)
return
end
local EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
local repairable = false
local srcstack = inv:get_stack("src", 1)
if not srcstack:is_empty() then
local itemdef = minetest.registered_items[srcstack:get_name()]
if itemdef and
(not itemdef.wear_represents or
itemdef.wear_represents == "mechanical_wear") and
srcstack:get_wear() ~= 0 then
repairable = true
end
end
technic.handle_machine_pipeworks(pos, tube_upgrade, function(pos2, x_velocity, z_velocity)
if not repairable then
technic.send_items(pos2, x_velocity, z_velocity, "src")
end
end)
if not repairable then
meta:set_string("infotext", S("@1 Idle", machine_name))
meta:set_int("MV_EU_demand", 0)
return
end
if eu_input < workshop_demand[EU_upgrade+1] then
meta:set_string("infotext", S("@1 Unpowered", machine_name))
elseif eu_input >= workshop_demand[EU_upgrade+1] then
meta:set_string("infotext", S("@1 Active", machine_name) .. "\n" ..
S("Demand: @1", technic.EU_string(workshop_demand[EU_upgrade+1])))
srcstack:add_wear(-1000)
inv:set_stack("src", 1, srcstack)
end
meta:set_int("MV_EU_demand", workshop_demand[EU_upgrade+1])
end
minetest.register_node("technic:tool_workshop", {
description = S("@1 Tool Workshop", S("MV")),
paramtype2 = "facedir",
tiles = {
"technic_workshop_top.png"..tube_entry,
"technic_machine_bottom.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"
},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_mv=1, tubedevice=1, tubedevice_receiver=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom", "back", "left", "right"},
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Tool Workshop", S("MV")))
meta:set_string("formspec", workshop_formspec)
local inv = meta:get_inventory()
inv:set_size("src", 1)
inv:set_size("upgrade1", 1)
inv:set_size("upgrade2", 1)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
tube = {
can_insert = function (pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():room_for_item("src", stack)
end,
insert_object = function (pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():add_item("src", stack)
end,
connect_sides = {left = 1, right = 1, back = 1, top = 1, bottom = 1},
},
technic_run = run,
after_place_node = pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node
})
technic.register_machine("MV", "technic:tool_workshop", technic.receiver)

View file

@ -0,0 +1,102 @@
local S = technic.getter
minetest.register_craft({
output = 'technic:wind_mill_frame 5',
recipe = {
{'technic:carbon_steel_ingot', '', 'technic:carbon_steel_ingot'},
{'', 'technic:carbon_steel_ingot', ''},
{'technic:carbon_steel_ingot', '', 'technic:carbon_steel_ingot'},
}
})
minetest.register_craft({
output = 'technic:wind_mill',
recipe = {
{'', 'basic_materials:motor', ''},
{'technic:carbon_steel_ingot', 'technic:carbon_steel_block', 'technic:carbon_steel_ingot'},
{'', 'technic:mv_cable', ''},
}
})
minetest.register_node("technic:wind_mill_frame", {
description = S("Wind Mill Frame"),
drawtype = "glasslike_framed",
tiles = {"technic_carbon_steel_block.png", "default_glass.png"},
sunlight_propagates = true,
groups = {cracky=3, pickaxey=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
sounds = technic.sounds.node_sound_stone_defaults(),
paramtype = "light",
})
local function check_wind_mill(pos)
if pos.y < 30 then
return false
end
pos = {x=pos.x, y=pos.y, z=pos.z}
for i = 1, 20 do
pos.y = pos.y - 1
local node = minetest.get_node_or_nil(pos)
if not node then
-- we reached CONTENT_IGNORE, we can assume, that nothing changed
-- as the user will have to load the block to change it
return
end
if node.name ~= "technic:wind_mill_frame" then
return false
end
end
return true
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local machine_name = S("Wind @1 Generator", S("MV"))
local check = check_wind_mill(pos)
if check == false then
meta:set_int("MV_EU_supply", 0)
meta:set_string("infotext", S("@1 Improperly Placed", machine_name))
elseif check == true then
local power = math.min(pos.y * 100, 5000)
meta:set_int("MV_EU_supply", power)
meta:set_string("infotext", S("@1 (@2)", machine_name,
technic.EU_string(power)))
end
-- check == nil: assume nothing has changed
end
minetest.register_node("technic:wind_mill", {
description = S("Wind @1 Generator", S("MV")),
tiles = {"technic_carbon_steel_block.png"},
paramtype2 = "facedir",
groups = {cracky=1, technic_machine=1, technic_mv=1, pickaxey=2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"top", "bottom", "back", "left", "right"},
sounds = technic.sounds.node_sound_stone_defaults(),
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, -- Main box
{-0.1, -0.1, -0.5, 0.1, 0.1, -0.6}, -- Shaft
{-0.1, -1, -0.6, 0.1, 1, -0.7}, -- Vertical blades
{-1, -0.1, -0.6, 1, 0.1, -0.7}, -- Horizontal blades
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Wind @1 Generator", S("MV")))
meta:set_int("MV_EU_supply", 0)
end,
technic_run = run,
})
technic.register_machine("MV", "technic:wind_mill", technic.producer)

View file

@ -0,0 +1,87 @@
local S = technic.getter
-- Registration compatibility shim to transform Technic 1.x arguments to 2.x
-- This could be made stricter for new style API by utilizing `assert`
local function shallow_copy(t)
local result = {}
for k, v in pairs(t) do
result[k] = v
end
return setmetatable(result, getmetatable(t))
end
function technic.register_compat_v1_to_v2(name, data, default_name)
local modname = minetest.get_current_modname()
local colon, def
if type(name) == "table" then
-- Log old API usage, swap name to def and set name from def table
local msg = "Deprecated Technic registration API call: %s (%s)"
def = shallow_copy(name)
name = def.machine_name or default_name
def.machine_name = nil
def.description = def.machine_desc
def.machine_desc = nil
minetest.log("warning", msg:format(tostring(name), tostring(modname)))
else
def = shallow_copy(data)
end
-- Input name can be "modname:nodename", ":modname:nodename" or "nodename".
-- If name is presented as "nodename" then check for old def.modname field.
if name:find(":") then
colon, modname, name = name:match("(:?)(.+):(.+)")
-- Make sure that all fields are set, can be empty but pattern matcher must succeed.
assert(colon and modname and name)
elseif def.modname then
minetest.log("warning", ("Definition contains modname for %s"):format(name))
colon = ":"
modname = def.modname
end
return (colon or ""), modname, name, def
end
-- Registration functions in Technic 1.x version
function technic.register_alloy_furnace(def)
def.typename = "alloy"
def.description = S("@1 Alloy Furnace", S(def.tier))
def.insert_object = technic.insert_object_unique_stack
def.can_insert = technic.can_insert_unique_stack
technic.register_base_machine(def)
end
function technic.register_centrifuge(def)
def.typename = "separating"
def.description = S("@1 Centrifuge", S(def.tier))
technic.register_base_machine(def)
end
function technic.register_compressor(def)
def.typename = "compressing"
def.description = S("@1 Compressor", S(def.tier))
technic.register_base_machine(def)
end
function technic.register_extractor(def)
def.typename = "extracting"
def.description = S("@1 Extractor", S(def.tier))
technic.register_base_machine(def)
end
function technic.register_freezer(def)
def.typename = "freezing"
def.description = S("@1 Freezer", S(def.tier))
technic.register_base_machine(def)
end
function technic.register_grinder(def)
def.typename = "grinding"
def.description = S("@1 Grinder", S(def.tier))
technic.register_base_machine(def)
end
function technic.register_electric_furnace(def)
def.typename = "cooking"
def.description = S("@1 Furnace", S(def.tier))
technic.register_base_machine(def)
end

View file

@ -0,0 +1,109 @@
--
-- Compatibility hacks for digtron to work well with new Technic Plus network and power tools
--
-- More information:
-- https://github.com/mt-mods/technic/issues/100
-- https://github.com/mt-mods/technic/issues/233
--
-- Disable some luacheck warnings to allow having original formatting here
-- luacheck: no max line length
-- luacheck: globals digtron
-- Only relevant sections modified, you can directly compare this with upstream function defined in util.lua
local node_inventory_table = {type="node"}
local function tap_batteries(battery_positions, target, test)
if (battery_positions == nil) then
return 0
end
local current_burned = 0
-- 1 coal block is 370 PU
-- 1 coal lump is 40 PU
-- An RE battery holds 10000 EU of charge
-- local power_ratio = 100 -- How much charge equals 1 unit of PU from coal
-- setting Moved to digtron.config.power_ratio
for k, location in pairs(battery_positions) do
if current_burned > target then
break
end
node_inventory_table.pos = location.pos
local inv = minetest.get_inventory(node_inventory_table)
local invlist = inv:get_list("batteries")
if (invlist == nil) then -- This check shouldn't be needed, it's yet another guard against https://github.com/minetest/minetest/issues/8067
break
end
for i, itemstack in pairs(invlist) do
local charge = technic.get_charge(itemstack)
local power_available = math.floor(charge / digtron.config.power_ratio)
if power_available ~= 0 then
local actual_burned = power_available -- we just take all we have from the battery, since they aren't stackable
-- don't bother recording the items if we're just testing, nothing is actually being removed.
if test ~= true then
-- since we are taking everything, the wear and charge can both be set to 0
technic.set_charge(itemstack, 0)
end
current_burned = current_burned + actual_burned
end
if current_burned > target then
break
end
end
if test ~= true then
-- only update the list if we're doing this for real.
inv:set_list("batteries", invlist)
end
end
return current_burned
end
local function power_connector_compat()
local digtron_technic_run = minetest.registered_nodes["digtron:power_connector"].technic_run
minetest.override_item("digtron:power_connector",{
technic_run = function(pos, node)
local network_id = technic.pos2network(pos)
local sw_pos = network_id and technic.network2sw_pos(network_id)
local meta = minetest.get_meta(pos)
meta:set_string("HV_network", sw_pos and minetest.pos_to_string(sw_pos) or "")
return digtron_technic_run(pos, node)
end,
technic_on_disable = function(pos, node)
local meta = minetest.get_meta(pos)
meta:set_string("HV_network", "")
meta:set_string("HV_EU_input", "")
end,
})
end
local function battery_holder_compat()
-- Override battery holder
local tube = minetest.registered_nodes["digtron:battery_holder"].tube
tube.can_insert = function(pos, node, stack, direction)
if technic.get_charge(stack) > 0 then
local inv = minetest.get_meta(pos):get_inventory()
return inv:room_for_item("batteries", stack)
end
return false
end
minetest.override_item("digtron:battery_holder",{
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return (listname == "batteries" and technic.get_charge(stack) > 0) and stack:get_count() or 0
end,
tube = tube,
})
-- Override digtron.tap_batteries
digtron.tap_batteries = tap_batteries
end
minetest.register_on_mods_loaded(function()
if minetest.registered_nodes["digtron:power_connector"] then
power_connector_compat()
end
if minetest.registered_nodes["digtron:battery_holder"] then
battery_holder_compat()
end
end)

View file

@ -0,0 +1,139 @@
--
-- This allows using old style technic.register_power_tool tool registration function.
--
-- To make tool fully compatible replace `minetest.register_tool` with `technic.register_power_tool`
-- and add `technic_max_charge` field for tool definition.
-- Fields `wear_represents` and `on_refill` can be removed if using defaults.
--
-- Does not offer compatibility for charger mods: mods that charge or discharge registered power tools.
-- Compatibility for those can be achieved by using `<tooldef>.technic_get_charge` / `<tooldef>.technic_set_charge`.
--
local function compat_set_RE_wear(stack, charge)
local def = stack:get_definition()
if def.technic_wear_factor then
local wear = math.floor(charge * def.technic_wear_factor + 0.5)
stack:set_wear(wear > 0 and 65536 - wear or 0)
end
end
local function compat_technic_get_charge(stack)
local def = stack:get_definition()
if def.technic_max_charge then
local legacy_fields = minetest.deserialize(stack:get_meta():get_string("")) or {}
return legacy_fields.charge or 0, def.technic_max_charge
end
return 0, 0
end
local function compat_technic_set_charge(stack, charge)
compat_set_RE_wear(stack, charge)
local meta = stack:get_meta()
local legacy_fields = minetest.deserialize(meta:get_string("")) or {}
legacy_fields.charge = charge
meta:set_string("", minetest.serialize(legacy_fields))
end
-- This attempts to find out if mod is aware of technic.plus version property and marks mod as `plus_aware` if
-- it tries to read `technic.plus` value. Marked tools wont receive automatic metadata compatibility functions.
local plus_aware = {}
do
local original_mt = getmetatable(technic)
local mt = original_mt and table.copy(original_mt) or {}
local mt_index = mt.__index or rawget
local plus = technic.plus
rawset(technic, "plus", nil)
-- Extend `__index` lookup function for `technic` to collect mod names that are reading `technic.plus` property
function mt:__index(key)
if key == "plus" then
local modname = minetest.get_current_modname()
if modname then
plus_aware[modname] = true
end
return plus
end
return mt_index(self, key)
end
setmetatable(technic, mt)
-- Restore version and metatable, this must happen after handling register_on_mods_loaded callbacks
minetest.after(0, function()
rawset(technic, "plus", plus)
setmetatable(technic, original_mt)
end)
end
-- Override `technic.register_power_tool` to handle old style registration that was only setting max charge value.
local register_power_tool = technic.register_power_tool
function technic.register_power_tool(itemname, def_or_max_charge)
if type(def_or_max_charge) == "number" then
minetest.log("warning", "Deprecated technic.register_power_tool use. Setting max_charge for "..itemname)
technic.power_tools[itemname] = def_or_max_charge
minetest.register_on_mods_loaded(function()
minetest.log("warning", "Deprecated technic.register_power_tool use. Ensuring fields for "..itemname)
local redef = minetest.registered_items[itemname]
if redef and redef.wear_represents == "technic_RE_charge" then
-- Override power tools that called register_power_tool but do not have on_refill function defined
local overrides = {
technic_max_charge = def_or_max_charge,
technic_wear_factor = 65535 / def_or_max_charge,
on_refill = function(stack)
local tooldef = stack:get_definition()
tooldef.technic_set_charge(stack, def_or_max_charge)
return stack
end,
}
-- Add legacy meta handlers if mod did not attempt to read technic.plus value
local modname = itemname:match(":?(.+):")
if plus_aware[modname] then
overrides.technic_get_charge = redef.technic_get_charge or technic.get_charge
overrides.technic_set_charge = redef.technic_set_charge or technic.set_charge
minetest.log("warning", "Mod "..modname.." seems to be aware of technic.plus but "..
itemname.." is still using deprecated registration, skipping meta charge compatibility.")
elseif not redef.technic_get_charge and not redef.technic_set_charge then
overrides.technic_get_charge = compat_technic_get_charge
overrides.technic_set_charge = compat_technic_set_charge
minetest.log("warning", "Using metadata charge values for "..itemname)
end
-- Override tool definition with added / new values
minetest.override_item(itemname, overrides)
minetest.log("warning", "Updated legacy Technic power tool definition for "..itemname)
else
minetest.log("error", "Technic compatibility overrides skipped for "..itemname..", charging might "..
'cause crash. Upgrading to technic.register_power_tool("'..itemname..'", {itemdef}) recommended.')
end
end)
else
return register_power_tool(itemname, def_or_max_charge)
end
end
technic.set_RE_charge = assert(technic.set_charge)
technic.get_RE_charge = assert(technic.get_charge)
technic.use_RE_charge = assert(technic.use_charge)
-- Same as `technic.set_charge` but without calling through `itemdef.technic_set_charge`.
function technic.set_RE_wear(stack, charge)
minetest.log("warning", "Use of deprecated function technic.set_RE_wear with stack: "..stack:get_name())
compat_set_RE_wear(stack, charge)
end
-- Old utility function to recharge tools
local function charge_tools(meta, batt_charge, charge_step)
local src_stack = meta:get_inventory():get_stack("src", 1)
local def = src_stack:get_definition()
if not def or not def.technic_max_charge or src_stack:is_empty() then
return batt_charge, false
end
local new_charge = math.min(def.technic_max_charge, def.technic_get_charge(src_stack) + charge_step)
def.technic_set_charge(src_stack, new_charge)
meta:get_inventory():set_stack("src", 1, src_stack)
return batt_charge, (def.technic_max_charge == new_charge)
end
function technic.charge_tools(...)
minetest.log("warning", "Use of deprecated function technic.charge_tools")
technic.charge_tools = charge_tools
return charge_tools(...)
end

View file

@ -0,0 +1,80 @@
local path = technic.modpath.."/machines"
technic.digilines = {
rules = {
-- digilines.rules.default
{x= 1,y= 0,z= 0},{x=-1,y= 0,z= 0}, -- along x beside
{x= 0,y= 0,z= 1},{x= 0,y= 0,z=-1}, -- along z beside
{x= 1,y= 1,z= 0},{x=-1,y= 1,z= 0}, -- 1 node above along x diagonal
{x= 0,y= 1,z= 1},{x= 0,y= 1,z=-1}, -- 1 node above along z diagonal
{x= 1,y=-1,z= 0},{x=-1,y=-1,z= 0}, -- 1 node below along x diagonal
{x= 0,y=-1,z= 1},{x= 0,y=-1,z=-1}, -- 1 node below along z diagonal
-- added rules for digi cable
{x = 0, y = -1, z = 0}, -- along y below
},
rules_allfaces = {
{x= 1, y= 0, z= 0}, {x=-1, y= 0, z= 0}, -- along x beside
{x= 0, y= 1, z= 0}, {x= 0, y=-1, z= 0}, -- along y above and below
{x= 0, y= 0, z= 1}, {x= 0, y= 0, z=-1}, -- along z beside
}
}
-- Compatibility shim to allow old API usage
dofile(path.."/compat/api.lua")
-- https://github.com/mt-mods/technic/issues/100
dofile(path.."/compat/digtron.lua")
dofile(path.."/network.lua")
dofile(path.."/overload.lua")
dofile(path.."/register/init.lua")
-- Tiers
dofile(path.."/LV/init.lua")
dofile(path.."/MV/init.lua")
dofile(path.."/HV/init.lua")
dofile(path.."/switching_station.lua")
dofile(path.."/switching_station_globalstep.lua")
dofile(path.."/power_monitor.lua")
dofile(path.."/supply_converter.lua")
dofile(path.."/other/init.lua")
-- Metadata cleanup LBM, removes old metadata values from nodes
minetest.register_lbm({
name = "technic:metadata_cleanup",
nodenames = {
"group:technic_machine",
"group:technic_all_tiers",
"technic:switching_station",
"technic:power_monitor",
},
action = function(pos, node)
-- Delete all listed metadata key/value pairs from technic machines
local keys = {
"LV_EU_timeout", "MV_EU_timeout", "HV_EU_timeout",
"LV_network", "MV_network", "HV_network",
"active_pos", "supply", "demand",
"battery_count", "battery_charge", "battery_charge_max",
}
local meta = minetest.get_meta(pos)
for _,key in ipairs(keys) do
-- Value of `""` will delete the key.
meta:set_string(key, "")
end
if node.name == "technic:switching_station" then
meta:set_string("active", "")
-- start nodetimer if not already started
local timer = minetest.get_node_timer(pos)
if not timer:is_started() then
timer:start(1.0)
end
end
end,
})

View file

@ -0,0 +1,767 @@
--
-- Power network specific functions and data should live here
--
local S = technic.getter
local off_delay_seconds = tonumber(technic.config:get("switch_off_delay_seconds"))
local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
technic.active_networks = {}
local networks = {}
technic.networks = networks
local technic_cables = {}
technic.cables = technic_cables
local poshash = minetest.hash_node_position
local hashpos = minetest.get_position_from_hash
function technic.create_network(sw_pos)
local network_id = poshash({x=sw_pos.x,y=sw_pos.y-1,z=sw_pos.z})
technic.build_network(network_id)
return network_id
end
local function pos_in_array(pos, array)
for _,pos2 in ipairs(array) do
if pos.x == pos2.x and pos.y == pos2.y and pos.z == pos2.z then
return true
end
end
return false
end
function technic.merge_networks(net1, net2)
-- TODO: Optimize for merging small network into larger by first checking network
-- node counts for both networks and keep network id with most nodes.
assert(type(net1) == "table", "Invalid net1 for technic.merge_networks")
assert(type(net2) == "table", "Invalid net2 for technic.merge_networks")
assert(net1 ~= net2, "Deadlock recipe: net1 & net2 equals for technic.merge_networks")
-- Move data in cables table
for node_id,cable_net_id in pairs(technic_cables) do
if cable_net_id == net2.id then
technic_cables[node_id] = net1.id
end
end
-- Move data in machine tables
for _,tablename in ipairs(network_node_arrays) do
for _,pos in ipairs(net2[tablename]) do
table.insert(net1[tablename], pos)
end
end
-- Move data in all_nodes table
for node_id,pos in pairs(net2.all_nodes) do
net1.all_nodes[node_id] = pos
end
-- Merge queues for incomplete networks
if net1.queue and net2.queue then
for _,pos in ipairs(net2.queue) do
if not pos_in_array(pos, net1.queue) then
table.insert(net1.queue, pos)
end
end
else
net1.queue = net1.queue or net2.queue
end
-- Remove links to net2
networks[net2.id] = nil
technic.active_networks[net2.id] = nil
end
function technic.activate_network(network_id, timeout)
-- timeout is optional ttl for network in seconds, if not specified use default
local network = networks[network_id]
if network then
-- timeout is absolute time in microseconds
network.timeout = minetest.get_us_time() + ((timeout or off_delay_seconds) * 1000 * 1000)
technic.active_networks[network_id] = network
end
end
function technic.sw_pos2tier(pos, load_node)
-- Get cable tier for switching station or nil if no cable
-- load_node true to use minetest.load_area to load node
local cable_pos = {x=pos.x,y=pos.y-1,z=pos.z}
if load_node then
minetest.load_area(cable_pos)
end
return technic.get_cable_tier(minetest.get_node(cable_pos).name)
end
-- Destroy network data
function technic.remove_network(network_id)
for pos_hash,cable_net_id in pairs(technic_cables) do
if cable_net_id == network_id then
technic_cables[pos_hash] = nil
end
end
networks[network_id] = nil
technic.active_networks[network_id] = nil
end
local function switch_index(pos, net)
for index, spos in ipairs(net.swpos) do
if pos.x == spos.x and pos.y == spos.y and pos.z == spos.z then
return index
end
end
end
function technic.switch_insert(pos, net)
if not switch_index(pos, net) then
table.insert(net.swpos, table.copy(pos))
end
return #net.swpos
end
function technic.switch_remove(pos, net)
local swindex = switch_index(pos, net)
if swindex then
table.remove(net.swpos, swindex)
end
return #net.swpos
end
function technic.sw_pos2network(pos)
return technic_cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
end
function technic.pos2network(pos)
return technic_cables[poshash(pos)]
end
function technic.network2pos(network_id)
return hashpos(network_id)
end
function technic.network2sw_pos(network_id)
-- Return switching station position for network.
-- It is not guaranteed that position actually contains switching station.
local sw_pos = hashpos(network_id)
sw_pos.y = sw_pos.y + 1
return sw_pos
end
function technic.network_infotext(network_id, text)
local network = networks[network_id]
if network then
if text then
network.infotext = text
elseif network.queue then
local count = 0
for _ in pairs(network.all_nodes) do count = count + 1 end
return S("Building Network: @1 Nodes", count)
else
return network.infotext
end
end
end
local node_timeout = {}
local default_timeout = 2
function technic.set_default_timeout(timeout)
default_timeout = timeout or 2
end
function technic.get_timeout(tier, pos)
if node_timeout[tier] == nil then
-- it is normal that some multi tier nodes always drop here when checking all LV, MV and HV tiers
return 0
end
return node_timeout[tier][poshash(pos)] or 0
end
local function touch_node(tier, pos, timeout)
if node_timeout[tier] == nil then
-- this should get built up during registration
node_timeout[tier] = {}
end
node_timeout[tier][poshash(pos)] = timeout or default_timeout
end
technic.touch_node = touch_node
function technic.disable_machine(pos, node)
local nodedef = minetest.registered_nodes[node.name]
if nodedef then
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Has No Network", nodedef.description))
end
if nodedef and nodedef.technic_disabled_machine_name then
node.name = nodedef.technic_disabled_machine_name
minetest.swap_node(pos, node)
end
if nodedef and nodedef.technic_on_disable then
nodedef.technic_on_disable(pos, node)
end
local node_id = poshash(pos)
for _,nodes in pairs(node_timeout) do
nodes[node_id] = nil
end
end
local function match_cable_tier_filter(name, tiers)
-- Helper to check for set of cable tiers
if tiers then
for _, tier in ipairs(tiers) do if technic.is_tier_cable(name, tier) then return true end end
return false
end
return technic.get_cable_tier(name) ~= nil
end
local function get_neighbors(pos, tiers)
local tier_machines = tiers and technic.machines[tiers[1]]
local is_cable = match_cable_tier_filter(minetest.get_node(pos).name, tiers)
local network = is_cable and technic.networks[technic.pos2network(pos)]
local cables = {}
local machines = {}
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _,connected_pos in ipairs(positions) do
local name = minetest.get_node(connected_pos).name
if tier_machines and tier_machines[name] then
table.insert(machines, connected_pos)
elseif match_cable_tier_filter(name, tiers) then
local cable_network = technic.networks[technic.pos2network(connected_pos)]
table.insert(cables,{
pos = connected_pos,
network = cable_network,
})
if not network then network = cable_network end
end
end
return network, cables, machines
end
function technic.place_network_node(pos, tiers, name)
-- Get connections and primary network if there's any
local network, cables, machines = get_neighbors(pos, tiers)
if not network then
-- We're evidently not on a network, nothing to add ourselves to
return
end
-- Attach to primary network, this must be done before building branches from this position
technic.add_network_node(pos, network)
if not match_cable_tier_filter(name, tiers) then
if technic.machines[tiers[1]][name] == technic.producer_receiver then
-- FIXME: Multi tier machine like supply converter should also attach to other networks around pos.
-- Preferably also with connection rules defined for machine.
-- nodedef.connect_sides could be used to generate these rules.
-- For now, assume that all multi network machines belong to technic.producer_receiver group:
-- Get cables and networks around PR_RE machine
local _, machine_cables, _ = get_neighbors(pos)
for _,connection in ipairs(machine_cables) do
if connection.network and connection.network.id ~= network.id then
-- Attach PR_RE machine to secondary networks (last added is primary until above note is resolved)
technic.add_network_node(pos, connection.network)
end
end
else
-- Check connected cables for foreign networks, overload if machine was connected to multiple networks
for _, connection in ipairs(cables) do
if connection.network and connection.network.id ~= network.id then
technic.overload_network(connection.network.id)
technic.overload_network(network.id)
end
end
end
-- Machine added, skip all network building
return
end
-- Attach neighbor machines if cable was added
for _,machine_pos in ipairs(machines) do
technic.add_network_node(machine_pos, network)
end
-- Attach neighbor cables
for _,connection in ipairs(cables) do
if connection.network then
if connection.network.id ~= network.id then
-- Remove network if position belongs to another network
-- FIXME: Network requires partial rebuild but avoid doing it here if possible.
-- This might cause problems when merging two active networks into one
technic.remove_network(network.id)
technic.remove_network(connection.network.id)
connection.network = nil
end
else
-- There's cable that does not belong to any network, attach whole branch
technic.add_network_node(connection.pos, network)
technic.add_network_branch({connection.pos}, network)
end
end
end
-- Remove machine or cable from network
local function remove_network_node(network_id, pos)
local network = networks[network_id]
if not network then return end
-- Clear hash tables, cannot use table.remove
local node_id = poshash(pos)
technic_cables[node_id] = nil
network.all_nodes[node_id] = nil
-- TODO: All following things can be skipped if node is not machine
-- check here if it is or is not cable
-- or add separate function to remove cables and move responsibility to caller
-- Clear indexed arrays, do NOT leave holes
local machine_removed = false
for _,tblname in ipairs(network_node_arrays) do
local tbl = network[tblname]
for i=#tbl,1,-1 do
local mpos = tbl[i]
if mpos.x == pos.x and mpos.y == pos.y and mpos.z == pos.z then
table.remove(tbl, i)
machine_removed = true
break
end
end
end
if machine_removed then
-- Machine can still be in world, just not connected to any network. If so then disable it.
local node = minetest.get_node(pos)
technic.disable_machine(pos, node)
end
end
function technic.remove_network_node(pos, tiers, name)
-- Get the network and neighbors
local network, cables, machines = get_neighbors(pos, tiers)
if not network then return end
if not match_cable_tier_filter(name, tiers) then
-- Machine removed, skip cable checks to prevent unnecessary network cleanups
for _,connection in ipairs(cables) do
if connection.network then
-- Remove machine from all networks around it
remove_network_node(connection.network.id, pos)
end
end
return
end
if #cables == 1 then
-- Dead end cable removed, remove it from the network
remove_network_node(network.id, pos)
-- Remove neighbor machines from network if cable was removed
if match_cable_tier_filter(name, tiers) then
for _,machine_pos in ipairs(machines) do
local net, _, _ = get_neighbors(machine_pos, tiers)
if not net then
-- Remove machine from network if it does not have other connected cables
remove_network_node(network.id, machine_pos)
end
end
end
else
-- TODO: Check branches around and switching stations for branches:
-- remove branches that do not have switching station. Switching stations not tracked but could be easily tracked.
-- remove branches not connected to another branch. Individual branches not tracked, requires simple AI heuristics.
-- move branches that have switching station to new networks without checking or loading actual nodes in world.
-- To do all this network must be aware of individual branches and switching stations, might not be worth it...
-- For now remove whole network and let ABM rebuild it
technic.remove_network(network.id)
end
end
--
-- Functions to traverse the electrical network
--
-- Add a machine node to the LV/MV/HV network
local function add_network_machine(nodes, pos, network_id, all_nodes, multitier)
local node_id = poshash(pos)
local net_id_old = technic_cables[node_id]
if net_id_old == nil or (multitier and net_id_old ~= network_id and all_nodes[node_id] == nil) then
-- Add machine to network only if it is not already added
table.insert(nodes, pos)
-- FIXME: Machines connecting to multiple networks should have way to store multiple network ids
technic_cables[node_id] = network_id
all_nodes[node_id] = pos
return true
elseif not multitier and net_id_old ~= network_id then
-- Do not allow running from multiple networks, trigger overload
technic.overload_network(network_id)
technic.overload_network(net_id_old)
local meta = minetest.get_meta(pos)
meta:set_string("infotext",S("Network Overloaded"))
end
end
-- Add a wire node to the LV/MV/HV network
local function add_cable_node(pos, network)
local node_id = poshash(pos)
if not technic_cables[node_id] then
technic_cables[node_id] = network.id
network.all_nodes[node_id] = pos
if network.queue then
table.insert(network.queue, pos)
end
elseif technic_cables[node_id] ~= network.id then
-- Conflicting network connected, merge networks if both are still in building stage
local net2 = networks[technic_cables[node_id]]
if net2 and net2.queue then
technic.merge_networks(network, net2)
end
end
end
-- Generic function to add found connected nodes to the right classification array
local function add_network_node(network, pos, machines)
local name = technic.get_or_load_node(pos).name
if technic.get_cable_tier(name) == network.tier then
add_cable_node(pos, network)
elseif machines[name] then
if machines[name] == technic.producer then
add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.receiver then
add_network_machine(network.RE_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.producer_receiver then
if add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes, true) then
table.insert(network.RE_nodes, pos)
end
elseif machines[name] == technic.battery then
add_network_machine(network.BA_nodes, pos, network.id, network.all_nodes)
end
end
end
-- Generic function to add single nodes to the right classification array of existing network
function technic.add_network_node(pos, network)
add_network_node(network, pos, technic.machines[network.tier])
end
-- Traverse a network given a list of machines and a cable type name
local function traverse_network(network, pos, machines)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1}}
for i, cur_pos in pairs(positions) do
if not network.all_nodes[poshash(cur_pos)] then
add_network_node(network, cur_pos, machines)
end
end
end
local function touch_nodes(list, tier)
for _, pos in ipairs(list) do
touch_node(tier, pos) -- Touch node
end
end
local function get_network(network_id, tier)
local cached = networks[network_id]
if cached and not cached.queue and cached.tier == tier then
touch_nodes(cached.PR_nodes, tier)
touch_nodes(cached.BA_nodes, tier)
touch_nodes(cached.RE_nodes, tier)
return cached.PR_nodes, cached.BA_nodes, cached.RE_nodes
end
return technic.build_network(network_id)
end
function technic.add_network_branch(queue, network)
-- Adds whole branch to network, queue positions can be used to bypass sub branches
local machines = technic.machines[network.tier]
--print(string.format("technic.add_network_branch(%s, %s, %.17g)",queue,minetest.pos_to_string(sw_pos),network.id))
local t1 = minetest.get_us_time()
while next(queue) do
local to_visit = {}
for _, pos in ipairs(queue) do
network.queue = to_visit
traverse_network(network, pos, machines)
end
queue = to_visit
if minetest.get_us_time() - t1 > 10000 then
-- time limit exceeded
break
end
end
-- Set build queue for network if network build was not finished within time limits
network.queue = #queue > 0 and queue
end
-- Battery charge status updates for network
local function update_battery(self, charge, max_charge, supply, demand)
self.battery_charge = self.battery_charge + charge
self.battery_charge_max = self.battery_charge_max + max_charge
self.battery_supply = self.battery_supply + supply
self.battery_demand = self.battery_demand + demand
if demand ~= 0 then
self.BA_count_active = self.BA_count_active + 1
self.BA_charge_active = self.BA_charge_active + charge
end
end
-- Moving average function generator
local function sma(period)
local values = {}
local index = 1
local sum = 0
return function(n)
-- Add new value and return average
sum = sum - (values[index] or 0) + n
values[index] = n
index = index ~= period and index + 1 or 1
return sum / #values
end
end
function technic.build_network(network_id)
local network = networks[network_id]
if network and not network.queue then
-- Network exists complete and cached
return network.PR_nodes, network.BA_nodes, network.RE_nodes
elseif not network then
-- Build new network if network does not exist
technic.remove_network(network_id)
local sw_pos = technic.network2sw_pos(network_id)
local tier = sw_pos and technic.sw_pos2tier(sw_pos)
if not tier then
-- Failed to get initial cable node for network
return
end
network = {
-- Build queue
queue = {},
-- Basic network data and lookup table for attached nodes
id = network_id, tier = tier, all_nodes = {}, swpos = {},
-- Indexed arrays for iteration by machine type
PR_nodes = {}, RE_nodes = {}, BA_nodes = {},
-- Power generation, usage and capacity related variables
supply = 0, demand = 0, battery_charge = 0, battery_charge_max = 0,
BA_count_active = 0, BA_charge_active = 0, battery_supply = 0, battery_demand = 0,
-- Battery status update function
update_battery = update_battery,
-- Network activation and excution control
timeout = 0, skip = 0, lag = 0, average_lag = sma(5)
}
-- Add first cable (one that is holding network id) and build network
add_cable_node(technic.network2pos(network_id), network)
end
-- Continue building incomplete network
technic.add_network_branch(network.queue, network)
network.battery_count = #network.BA_nodes
-- Add newly built network to cache array
networks[network_id] = network
if not network.queue then
-- And return producers, batteries and receivers (should this simply return network?)
return network.PR_nodes, network.BA_nodes, network.RE_nodes
end
end
--
-- Execute technic power network
--
local node_technic_run = {}
minetest.register_on_mods_loaded(function()
for name, tiers in pairs(technic.machine_tiers) do
local nodedef = minetest.registered_nodes[name]
local on_construct = type(nodedef.on_construct) == "function" and nodedef.on_construct
local on_destruct = type(nodedef.on_destruct) == "function" and nodedef.on_destruct
local place_node = technic.place_network_node
local remove_node = technic.remove_network_node
minetest.override_item(name, {
on_construct = on_construct
and function(pos) on_construct(pos) place_node(pos, tiers, name) end
or function(pos) place_node(pos, tiers, name) end,
on_destruct = on_destruct
and function(pos) on_destruct(pos) remove_node(pos, tiers, name) end
or function(pos) remove_node(pos, tiers, name) end,
})
end
for name, _ in pairs(technic.machine_tiers) do
if type(minetest.registered_nodes[name].technic_run) == "function" then
node_technic_run[name] = minetest.registered_nodes[name].technic_run
end
end
end)
local function run_nodes(list, run_stage, network)
for _, pos in ipairs(list) do
local node = minetest.get_node_or_nil(pos)
if not node then
minetest.load_area(pos, pos)
node = minetest.get_node_or_nil(pos)
end
if node and node.name and node_technic_run[node.name] then
node_technic_run[node.name](pos, node, run_stage, network)
end
end
end
function technic.network_run(network_id)
--
-- !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
-- TODO: This function requires a lot of cleanup
-- It is moved here from switching_station.lua and still
-- contain a lot of switching station specific stuff which
-- should be removed and/or refactored.
--
local t0 = minetest.get_us_time()
local PR_nodes
local BA_nodes
local RE_nodes
local network = networks[network_id]
if network then
PR_nodes, BA_nodes, RE_nodes = get_network(network_id, network.tier)
if not PR_nodes or technic.is_overloaded(network_id) then return end
else
--dprint("Not connected to a network")
technic.network_infotext(network_id, S("@1 Has No Network", S("Switching Station")))
return
end
-- Reset battery data for updates
network.battery_charge = 0
network.battery_charge_max = 0
network.battery_supply = 0
network.battery_demand = 0
network.BA_count_active = 0
network.BA_charge_active = 0
run_nodes(PR_nodes, technic.producer, network)
run_nodes(RE_nodes, technic.receiver, network)
run_nodes(BA_nodes, technic.battery, network)
-- Strings for the meta data
local eu_demand_str = network.tier.."_EU_demand"
local eu_input_str = network.tier.."_EU_input"
local eu_supply_str = network.tier.."_EU_supply"
-- Distribute charge equally across multiple batteries.
local charge_distributed = math.floor(network.BA_charge_active / network.BA_count_active)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
if (meta1:get_int(eu_demand_str) ~= 0) then
meta1:set_int("internal_EU_charge", charge_distributed)
end
end
-- Get all the power from the PR nodes
local PR_eu_supply = 0 -- Total power
for _, pos1 in pairs(PR_nodes) do
local meta1 = minetest.get_meta(pos1)
PR_eu_supply = PR_eu_supply + meta1:get_int(eu_supply_str)
end
--dprint("Total PR supply:"..PR_eu_supply)
-- Get all the demand from the RE nodes
local RE_eu_demand = 0
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
RE_eu_demand = RE_eu_demand + meta1:get_int(eu_demand_str)
end
--dprint("Total RE demand:"..RE_eu_demand)
technic.network_infotext(network_id, S("@1. Supply: @2 Demand: @3",
S("Switching Station"), technic.EU_string(PR_eu_supply),
technic.EU_string(RE_eu_demand)))
-- Data that will be used by the power monitor
network.supply = PR_eu_supply
network.demand = RE_eu_demand
network.battery_count = #BA_nodes
-- If the PR supply is enough for the RE demand supply them all
local BA_eu_demand = network.battery_demand
if PR_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply"..PR_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a surplus, so distribute the rest equally to the BA nodes
-- Let's calculate the factor of the demand
PR_eu_supply = PR_eu_supply - RE_eu_demand
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
-- TODO: EU input for all batteries: math.floor(BA_eu_demand * charge_factor * #BA_nodes)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
--dprint("Charging battery:"..math.floor(eu_demand*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [+supply] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
return
end
-- If the PR supply is not enough for the RE demand we will discharge the batteries too
local BA_eu_supply = network.battery_supply
if PR_eu_supply + BA_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply "..PR_eu_supply.."+BA_eu_supply "..BA_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a deficit, so distribute to the BA nodes
-- Let's calculate the factor of the supply
local charge_factor = 0 -- Assume all batteries depleted
if BA_eu_supply > 0 then
charge_factor = (PR_eu_supply - RE_eu_demand) / BA_eu_supply
end
for n,pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_supply = meta1:get_int(eu_supply_str)
meta1:set_int(eu_input_str, math.floor(eu_supply * charge_factor))
--dprint("Discharging battery:"..math.floor(eu_supply*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [-supply] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
return
end
-- If the PR+BA supply is not enough for the RE demand: Power only the batteries
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
end
for n, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
meta1:set_int(eu_input_str, 0)
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] technic_run took " .. diff .. " us at "
.. minetest.pos_to_string(hashpos(network_id)))
end
end

View file

@ -0,0 +1,131 @@
local S = technic.getter
local desc = S("Administrative World Anchor")
local function compute_forceload_positions(pos, meta)
local radius = meta:get_int("radius")
local minpos = vector.subtract(pos, vector.new(radius, radius, radius))
local maxpos = vector.add(pos, vector.new(radius, radius, radius))
local minbpos = {}
local maxbpos = {}
for _, coord in ipairs({"x","y","z"}) do
minbpos[coord] = math.floor(minpos[coord] / 16) * 16
maxbpos[coord] = math.floor(maxpos[coord] / 16) * 16
end
local flposes = {}
for x = minbpos.x, maxbpos.x, 16 do
for y = minbpos.y, maxbpos.y, 16 do
for z = minbpos.z, maxbpos.z, 16 do
table.insert(flposes, vector.new(x, y, z))
end
end
end
return flposes
end
local function currently_forceloaded_positions(meta)
local ser = meta:get_string("forceloaded")
return ser == "" and {} or minetest.deserialize(ser)
end
local function forceload_off(meta)
local flposes = currently_forceloaded_positions(meta)
meta:set_string("forceloaded", "")
for _, p in ipairs(flposes) do
minetest.forceload_free_block(p)
end
end
local function forceload_on(pos, meta)
local want_flposes = compute_forceload_positions(pos, meta)
local have_flposes = {}
for _, p in ipairs(want_flposes) do
if minetest.forceload_block(p) then
table.insert(have_flposes, p)
end
end
meta:set_string("forceloaded", #have_flposes == 0 and "" or minetest.serialize(have_flposes))
end
local function set_display(pos, meta)
meta:set_string("infotext", S(meta:get_int("enabled") ~= 0 and "@1 Enabled" or "@1 Disabled", desc))
meta:set_string("formspec",
"size[5,3.5]"..
"item_image[0,0;1,1;technic:admin_anchor]"..
"label[1,0;"..desc.."]"..
"label[0,1;"..S("Owner: @1", meta:get_string("owner")).."]"..
(meta:get_int("locked") == 0 and
"button[3,1;2,1;lock;"..S("Unlocked").."]" or
"button[3,1;2,1;unlock;"..S("Locked").."]")..
"field[0.25,2.3;1,1;radius;"..S("Radius")..";${radius}]"..
(meta:get_int("enabled") == 0 and
"button[3,2;2,1;enable;"..S("Disabled").."]" or
"button[3,2;2,1;disable;"..S("Enabled").."]")..
"label[0,3;"..S("Keeping @1/@2 map blocks loaded",
#currently_forceloaded_positions(meta), #compute_forceload_positions(pos, meta)).."]")
end
minetest.register_node("technic:admin_anchor", {
description = desc,
drawtype = "normal",
tiles = {"technic_admin_anchor.png"},
is_ground_content = false,
groups = {cracky=3, not_in_creative_inventory=1, pickaxey=1},
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
sounds = technic.sounds.node_sound_stone_defaults(),
after_place_node = function (pos, placer)
local meta = minetest.get_meta(pos)
if placer and placer:is_player() then
meta:set_string("owner", placer:get_player_name())
end
set_display(pos, meta)
end,
can_dig = function (pos, player)
local meta = minetest.get_meta(pos)
return meta:get_int("locked") == 0 or (player and player:is_player()
and player:get_player_name() == meta:get_string("owner"))
end,
on_destruct = function (pos)
local meta = minetest.get_meta(pos)
forceload_off(meta)
end,
on_receive_fields = function (pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
if (meta:get_int("locked") ~= 0 or fields.lock) and
not (sender and sender:is_player() and
sender:get_player_name() == meta:get_string("owner")) then
return
end
if fields.unlock then meta:set_int("locked", 0) end
if fields.lock then meta:set_int("locked", 1) end
if fields.disable or fields.enable or fields.radius then
forceload_off(meta)
if fields.disable then meta:set_int("enabled", 0) end
if fields.enable then meta:set_int("enabled", 1) end
if fields.radius and string.find(fields.radius, "^[0-9]+$") and tonumber(fields.radius) < 256 then
meta:set_int("radius", fields.radius)
end
if meta:get_int("enabled") ~= 0 then
forceload_on(pos, meta)
end
end
set_display(pos, meta)
end,
mesecons = {
effector = {
action_on = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 1)
forceload_on(pos, meta)
set_display(pos, meta)
end,
action_off = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 0)
forceload_off(meta)
set_display(pos, meta)
end
}
}
})

View file

@ -0,0 +1,206 @@
-- Fuel driven alloy furnace. This uses no EUs:
local S = technic.getter
local mat = technic.materials
minetest.register_craft({
output = 'technic:coal_alloy_furnace',
recipe = {
{mat.brick, mat.brick, mat.brick},
{mat.brick, '', mat.brick},
{mat.brick, mat.brick, mat.brick},
}
})
local machine_name = S("Fuel-Fired Alloy Furnace")
local size = minetest.get_modpath("mcl_formspec") and "size[9,9]" or "size[8,9]"
local formspec =
size..
"label[0,0;"..machine_name.."]"..
"image[2,2;1,1;default_furnace_fire_bg.png]"..
"list[context;fuel;2,3;1,1;]"..
"list[context;src;2,1;2,1;]"..
"list[context;dst;5,1;2,2;]"
if minetest.get_modpath("mcl_formspec") then
formspec = formspec..
mcl_formspec.get_itemslot_bg(2,3,1,1)..
mcl_formspec.get_itemslot_bg(2,1,2,1)..
mcl_formspec.get_itemslot_bg(5,1,2,2)..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)
else
formspec = formspec..
"list[current_player;main;0,5;8,4;]"
end
-- listrings
formspec = formspec..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
minetest.register_node("technic:coal_alloy_furnace", {
description = machine_name,
tiles = {"technic_coal_alloy_furnace_top.png", "technic_coal_alloy_furnace_bottom.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_side.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_front.png"},
paramtype2 = "facedir",
groups = {cracky=2, pickaxey=2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec)
meta:set_string("infotext", machine_name)
local inv = meta:get_inventory()
inv:set_size("fuel", 1)
inv:set_size("src", 2)
inv:set_size("dst", 4)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
})
minetest.register_node("technic:coal_alloy_furnace_active", {
description = machine_name,
tiles = {"technic_coal_alloy_furnace_top.png", "technic_coal_alloy_furnace_bottom.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_side.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_front_active.png"},
paramtype2 = "facedir",
light_source = 8,
drop = "technic:coal_alloy_furnace",
groups = {cracky=2, not_in_creative_inventory=1, pickaxey=2},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_stone_defaults(),
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
})
minetest.register_abm({
label = "Machines: run coal alloy furnace",
nodenames = {"technic:coal_alloy_furnace", "technic:coal_alloy_furnace_active"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local src_list = inv:get_list("src")
if not src_list then
return
end
for i, name in pairs({
"fuel_totaltime",
"fuel_time",
"src_totaltime",
"src_time"}) do
if not meta:get_float(name) then
meta:set_float(name, 0.0)
end
end
-- Get what to cook if anything
local recipe = technic.get_recipe("alloy", src_list)
local was_active = false
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
was_active = true
meta:set_int("fuel_time", meta:get_int("fuel_time") + 1)
if recipe then
meta:set_int("src_time", meta:get_int("src_time") + 1)
if meta:get_int("src_time") >= recipe.time then
meta:set_int("src_time", 0)
technic.process_recipe(recipe, inv)
end
else
meta:set_int("src_time", 0)
end
end
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
local percent = math.floor(meta:get_float("fuel_time") /
meta:get_float("fuel_totaltime") * 100)
meta:set_string("infotext", S("@1 Active", machine_name).." ("..percent.."%)")
technic.swap_node(pos, "technic:coal_alloy_furnace_active")
meta:set_string("formspec",
size..
"label[0,0;"..machine_name.."]"..
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100 - percent)..":default_furnace_fire_fg.png]"..
"list[context;fuel;2,3;1,1;]"..
"list[context;src;2,1;2,1;]"..
"list[context;dst;5,1;2,2;]"..
(minetest.get_modpath("mcl_formspec") and
mcl_formspec.get_itemslot_bg(2,3,1,1)..
mcl_formspec.get_itemslot_bg(2,1,2,1)..
mcl_formspec.get_itemslot_bg(5,1,2,2)..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)
or "list[current_player;main;0,5;8,4;]")..
-- listrings
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]")
return
end
if not technic.get_recipe("alloy", inv:get_list("src")) then
if was_active then
meta:set_string("infotext", S("@1 is empty", machine_name))
technic.swap_node(pos, "technic:coal_alloy_furnace")
meta:set_string("formspec", formspec)
end
return
end
-- Next take a hard look at the fuel situation
local fuellist = inv:get_list("fuel")
local fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
if fuel.time <= 0 then
meta:set_string("infotext", S("@1 Out Of Fuel", machine_name))
technic.swap_node(pos, "technic:coal_alloy_furnace")
meta:set_string("formspec", formspec)
return
end
meta:set_string("fuel_totaltime", fuel.time)
meta:set_string("fuel_time", 0)
inv:set_stack("fuel", 1, afterfuel.items[1])
end,
})

View file

@ -0,0 +1,7 @@
local S = technic.getter
local default_furnace = minetest.registered_nodes["default:furnace"]
if default_furnace and default_furnace.description == "Furnace" then
minetest.override_item("default:furnace", { description = S("Fuel-Fired Furnace") })
end

View file

@ -0,0 +1,246 @@
local S = technic.getter
local function deploy_node(inv, slot_name, pos, node, machine_node)
if node.param2 > 3 then return end
if node.name ~= "air" then
if node.name == "ignore" or
node.name == "default:chest_open" or
node.name == "default:chest_locked_open" or
node.name == "default:lava_source" or
node.name == "default:lava_flowing" or
node.name == "default:water_source" or
node.name == "default:water_flowing" then
return
end
local drops = minetest.get_node_drops(node.name, "")
local remove_to = false
for i, item in ipairs(drops) do
if not inv:room_for_item(slot_name, item) then
remove_to = i - 1
break
end
inv:add_item(slot_name, item)
end
if remove_to then
for i = 1, remove_to do
inv:remove_item(slot_name, drops[i])
end
else
minetest.remove_node(pos)
end
return
end
if not inv:is_empty(slot_name) then
local stack = inv:get_list(slot_name)[1]
local def = stack:get_definition()
if def.type == "node" then
minetest.set_node(pos, {
name = stack:get_name(),
param2 = machine_node.param2
})
stack:take_item()
inv:set_stack(slot_name, 1, stack)
elseif def.type == "craft" then
if def.on_place then
-- Use pcall to avoid nil placer errors.
-- TODO: Do without pcall.
local ok, stk = pcall(def.on_place, stack, nil, {
-- Fake pointed_thing
type = "node",
above = pos,
under = {x=pos.x, y=pos.y-1, z=pos.z},
})
if ok then
inv:set_stack(slot_name, 1, stk or stack)
return
end
end
minetest.item_place_object(stack, nil, {
-- Fake pointed_thing
type = "node",
above = pos,
under = pos,
})
inv:set_stack(slot_name, 1, nil)
end
end
end
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk1_off 1',
recipe = {'technic:nodebreaker_off', 'technic:deployer_off'},
})
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk2_off 1',
recipe = {'technic:constructor_mk1_off', 'technic:constructor_mk1_off'},
})
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk3_off 1',
recipe = {'technic:constructor_mk2_off', 'technic:constructor_mk2_off'},
})
local function make_on(mark, length)
return function(pos, node)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
local inv = meta:get_inventory()
local dir = vector.new()
if node.param2 == 3 then dir.x = 1 end
if node.param2 == 2 then dir.z = 1 end
if node.param2 == 1 then dir.x = -1 end
if node.param2 == 0 then dir.z = -1 end
local place_pos = vector.new(pos)
if node.name == "technic:constructor_mk"..mark.."_off" then
technic.swap_node(pos, "technic:constructor_mk"..mark.."_on")
minetest.check_for_falling(pos)
for i = 1, length do
place_pos = vector.add(place_pos, dir)
if owner ~= "" and minetest.is_protected(place_pos, owner) then
return
end
local place_node = minetest.get_node(place_pos)
deploy_node(inv, "slot"..i, place_pos, place_node, node)
end
end
end
end
local function make_off(mark)
return function(pos, node)
if node.name == "technic:constructor_mk"..mark.."_on" then
technic.swap_node(pos,"technic:constructor_mk"..mark.."_off")
minetest.check_for_falling(pos)
end
end
end
local function allow_inventory_put(pos, listname, index, stack, player)
if stack and minetest.get_item_group(stack:get_name(), "technic_constructor") == 1 then
return 0
end
return technic.machine_inventory_put(pos, listname, index, stack, player)
end
local function make_constructor(mark, length)
minetest.register_node("technic:constructor_mk"..mark.."_off", {
description = S("Constructor Mk@1", mark),
tiles = {"technic_constructor_mk"..mark.."_top_off.png",
"technic_constructor_mk"..mark.."_bottom_off.png",
"technic_constructor_mk"..mark.."_side2_off.png",
"technic_constructor_mk"..mark.."_side1_off.png",
"technic_constructor_back.png",
"technic_constructor_front_off.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
mesecon = 2, technic_constructor = 1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
mesecons = {effector = {action_on = make_on(mark, length)}},
sounds = technic.sounds.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local size = minetest.get_modpath("mcl_formspec") and "size[9,9]" or "size[8,9]"
local formspec = size..
"label[0,0;"..S("Constructor Mk@1", mark).."]"
for i = 1, length do
formspec = formspec..
"label[5,"..(i - 1)..";"..S("Slot @1", i).."]"..
"list[context;slot"..i..";6,"..(i - 1)..";1,1;]"
end
if minetest.get_modpath("mcl_formspec") then
for i = 1, length do
formspec = formspec..
mcl_formspec.get_itemslot_bg(6,i-1,1,1)
end
formspec = formspec..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)
else
formspec = formspec..
"list[current_player;main;0,5;8,4;]"
end
-- listrings
for i = 1, length do
formspec = formspec..
"listring[current_player;main]"..
"listring[context;slot"..i.."]"
end
meta:set_string("formspec", formspec)
meta:set_string("infotext", S("Constructor Mk@1", mark))
local inv = meta:get_inventory()
for i = 1, length do
inv:set_size("slot"..i, 1)
end
meta:set_string("owner", "?")
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", (placer and placer:get_player_name() or "?"))
end,
can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
for i = 1, length do
if not inv:is_empty("slot"..i) then
return false
end
end
return true
end,
allow_metadata_inventory_put = allow_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
on_rotate = function(pos, node, user, mode, new_param2)
if mode ~= 1 then
return false
end
end,
})
minetest.register_node("technic:constructor_mk"..mark.."_on", {
tiles = {"technic_constructor_mk"..mark.."_top_on.png",
"technic_constructor_mk"..mark.."_bottom_on.png",
"technic_constructor_mk"..mark.."_side2_on.png",
"technic_constructor_mk"..mark.."_side1_on.png",
"technic_constructor_back.png",
"technic_constructor_front_on.png"},
paramtype2 = "facedir",
drop = "technic:constructor_mk"..mark.."_off",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
mesecon=2, not_in_creative_inventory=1, technic_constructor=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
mesecons= {effector = {action_off = make_off(mark)}},
sounds = technic.sounds.node_sound_stone_defaults(),
allow_metadata_inventory_put = allow_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
on_rotate = false
})
end
make_constructor(1, 1)
make_constructor(2, 2)
make_constructor(3, 4)

View file

@ -0,0 +1,12 @@
local path = technic.modpath.."/machines/other"
-- Mesecons and tubes related
dofile(path.."/injector.lua")
dofile(path.."/constructor.lua")
-- Coal-powered machines
dofile(path.."/coal_alloy_furnace.lua")
dofile(path.."/coal_furnace.lua")
-- Force-loading
dofile(path.."/anchor.lua")

View file

@ -0,0 +1,180 @@
local S = technic.getter
local fs_helpers = pipeworks.fs_helpers
local tube_entry = "^pipeworks_tube_connection_metallic.png"
local mat = technic.materials
local param2_to_under = {
[0] = {x= 0,y=-1,z= 0}, [1] = {x= 0,y= 0,z=-1},
[2] = {x= 0,y= 0,z= 1}, [3] = {x=-1,y= 0,z= 0},
[4] = {x= 1,y= 0,z= 0}, [5] = {x= 0,y= 1,z= 0}
}
local size = minetest.get_modpath("mcl_formspec") and "size[9,10]" or "size[8,9]"
local base_formspec = size..
"label[0,0;"..S("Self-Contained Injector").."]"..
"list[context;main;0,2;8,2;]"..
"listring[context;main]"
if minetest.get_modpath("mcl_formspec") then
base_formspec = base_formspec..
mcl_formspec.get_itemslot_bg(0,2,8,2)..
-- player inventory
"list[current_player;main;0,5.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5.5,9,3)..
"list[current_player;main;0,8.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.74,9,1)..
"listring[current_player;main]"
else
base_formspec = base_formspec..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"
end
local function set_injector_formspec(pos)
local meta = minetest.get_meta(pos)
local formspec = base_formspec..
fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
if meta:get_string("mode") == "whole stacks" then
formspec = formspec.."button[0,1;4,1;mode_item;"..S("Stackwise").."]"
else
formspec = formspec.."button[0,1;4,1;mode_stack;"..S("Itemwise").."]"
end
if minetest.get_node_timer(pos):is_started() then
formspec = formspec.."button[4,1;4,1;disable;"..S("Enabled").."]"
else
formspec = formspec.."button[4,1;4,1;enable;"..S("Disabled").."]"
end
meta:set_string("formspec", formspec)
end
minetest.register_node("technic:injector", {
description = S("Self-Contained Injector"),
tiles = {
"technic_injector_top.png"..tube_entry,
"technic_injector_bottom.png",
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
tube = {
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return meta:get_inventory():room_for_item("main", stack)
end,
insert_object = function(pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():add_item("main", stack)
end,
connect_sides = {left=1, right=1, back=1, top=1, bottom=1},
},
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Self-Contained Injector"))
meta:set_string("mode", "single items")
meta:get_inventory():set_size("main", 16)
minetest.get_node_timer(pos):start(1)
set_injector_formspec(pos)
end,
can_dig = function(pos, player)
return minetest.get_meta(pos):get_inventory():is_empty("main")
end,
on_receive_fields = function(pos, formanme, fields, sender)
if fields.quit or not pipeworks.may_configure(pos, sender) then
return
end
local meta = minetest.get_meta(pos)
if fields.mode_item then
meta:set_string("mode", "single items")
elseif fields.mode_stack then
meta:set_string("mode", "whole stacks")
elseif fields.disable then
minetest.get_node_timer(pos):stop()
elseif fields.enable then
minetest.get_node_timer(pos):start(1)
end
fs_helpers.on_receive_fields(pos, fields)
set_injector_formspec(pos)
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local dir = param2_to_under[math.floor(node.param2 / 4)]
local node_under = minetest.get_node(vector.add(pos, dir))
if minetest.get_item_group(node_under.name, "tubedevice") > 0 then
local inv = meta:get_inventory()
local list = inv:get_list("main")
if not list then
return true
end
local stackwise = meta:get_string("mode") == "whole stacks"
for i,stack in ipairs(list) do
if not stack:is_empty() then
if stackwise then
technic.tube_inject_item(pos, pos, dir, stack:to_table())
stack:clear()
else
technic.tube_inject_item(pos, pos, dir, stack:take_item(1):to_table())
end
inv:set_stack("main", i, stack)
break
end
end
end
return true
end,
on_rotate = function(pos, node, user, mode, new_param2)
node.param2 = new_param2
minetest.swap_node(pos, node)
pipeworks.scan_for_tube_objects(pos)
return true
end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
after_place_node = pipeworks.after_place,
after_dig_node = pipeworks.after_dig
})
minetest.register_craft({
output = "technic:injector 1",
recipe = {
{"", "technic:control_logic_unit",""},
{"", mat.chest,""},
{"", "pipeworks:tube_1",""},
}
})
minetest.register_lbm({
label = "Old injector conversion",
name = "technic:old_injector_conversion",
nodenames = {"technic:injector"},
run_at_every_load = false,
action = function(pos, node)
minetest.get_node_timer(pos):start(1)
set_injector_formspec(pos)
end
})

View file

@ -0,0 +1,31 @@
--
-- Network overloading (incomplete cheat mitigation)
--
local overload_reset_time = technic.config:get_int("network_overload_reset_time")
local overloaded_networks = {}
local networks = technic.networks
function technic.overload_network(network_id)
local network = networks[network_id]
if network then
network.supply = 0
network.battery_charge = 0
end
overloaded_networks[network_id] = minetest.get_us_time() + (overload_reset_time * 1000 * 1000)
end
function technic.reset_overloaded(network_id)
local remaining = math.max(0, overloaded_networks[network_id] - minetest.get_us_time())
if remaining == 0 then
-- Clear cache, remove overload and restart network
technic.remove_network(network_id)
overloaded_networks[network_id] = nil
end
-- Returns 0 when network reset or remaining time if reset timer has not expired yet
return remaining
end
function technic.is_overloaded(network_id)
return overloaded_networks[network_id]
end

View file

@ -0,0 +1,133 @@
-- POWER MONITOR
-- The power monitor can be used to monitor how much power is available on a network,
-- similarly to the old "slave" switching stations.
local S = technic.getter
local mat = technic.materials
local cable_entry = "^technic_cable_connection_overlay.png"
-- Get registered cable or nil, returns nil if area is not loaded
local function get_cable(pos)
local node = minetest.get_node_or_nil(pos)
return (node and technic.get_cable_tier(node.name)) and node
end
-- return the position of connected cable or nil
-- TODO: Make it support every possible orientation
local function get_connected_cable_network(pos)
local param2 = minetest.get_node(pos).param2
-- should probably also work sideways or upside down but for now it wont
if param2 > 3 then return end
-- Below?
local checkpos = {x=pos.x,y=pos.y-1,z=pos.z}
local network_id = get_cable(checkpos) and technic.pos2network(checkpos)
if network_id then
return network_id
end
-- Behind?
checkpos = vector.add(minetest.facedir_to_dir(param2),pos)
network_id = get_cable(checkpos) and technic.pos2network(checkpos)
return network_id
end
-- return the position of the associated switching station or nil
local function get_network(pos)
local network_id = get_connected_cable_network(pos)
local network = network_id and technic.networks[network_id]
local swpos = network and technic.network2sw_pos(network_id)
local is_powermonitor = swpos and minetest.get_node(swpos).name == "technic:switching_station"
return (is_powermonitor and network.all_nodes[network_id]) and network
end
minetest.register_craft({
output = "technic:power_monitor",
recipe = {
{"", "", ""},
{"", "technic:machine_casing", mat.copper_ingot},
{"technic:lv_cable", "technic:lv_cable", "technic:lv_cable"}
}
})
minetest.register_node("technic:power_monitor",{
description = S("Power Monitor"),
tiles = {
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png"..cable_entry,
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png"..cable_entry,
"technic_power_monitor_front.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, technic_all_tiers=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom", "back"},
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Power Monitor"))
meta:set_string("formspec", "field[channel;"..S("Digiline Channel")..";${channel}]")
end,
on_receive_fields = function(pos, formname, fields, sender)
if not fields.channel then
return
end
local plname = sender:get_player_name()
if minetest.is_protected(pos, plname) and not minetest.check_player_privs(sender, "protection_bypass") then
minetest.record_protection_violation(pos, plname)
return
end
local meta = minetest.get_meta(pos)
meta:set_string("channel", fields.channel)
end,
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if msg ~= "GET" and msg ~= "get" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local network = get_network(pos)
if not network then return end
digilines.receptor_send(pos, technic.digilines.rules, channel, {
supply = network.supply,
demand = network.demand,
lag = network.lag,
battery_count = network.battery_count,
battery_charge = network.battery_charge,
battery_charge_max = network.battery_charge_max,
})
end
},
},
})
minetest.register_abm({
nodenames = {"technic:power_monitor"},
label = "Machines: run power monitor",
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.get_meta(pos)
local network = get_network(pos)
if network then
meta:set_string("infotext", S("Power Monitor. Supply: @1 Demand: @2",
technic.EU_string(network.supply), technic.EU_string(network.demand)))
else
meta:set_string("infotext",S("Power Monitor Has No Network"))
end
end,
})

View file

@ -0,0 +1,60 @@
local S = technic.getter
local mat = technic.materials
technic.register_recipe_type("alloy", {
description = S("Alloying"),
icon = "technic_mv_alloy_furnace_front.png",
input_size = 2,
})
function technic.register_alloy_recipe(data)
data.time = data.time or 6
technic.register_recipe("alloy", data)
end
local recipes = {
{"technic:copper_dust 7", "technic:tin_dust", mat.bronze_ingot.." 8", 12},
{mat.copper_ingot.." 7", mat.tin_ingot, mat.bronze_ingot.." 8", 12},
{"technic:wrought_iron_dust 2", "technic:coal_dust", "technic:carbon_steel_ingot 2", 6},
{"technic:wrought_iron_ingot 2", "technic:coal_dust", "technic:carbon_steel_ingot 2", 6},
{"technic:carbon_steel_dust 4", "technic:chromium_dust", "technic:stainless_steel_ingot 5", 7.5},
{"technic:carbon_steel_ingot 4", "technic:chromium_ingot", "technic:stainless_steel_ingot 5", 7.5},
{"technic:copper_dust 2", "technic:zinc_dust", "basic_materials:brass_ingot 3"},
{mat.copper_ingot.." 2", "technic:zinc_ingot", "basic_materials:brass_ingot 3"},
{mat.sand.." 2", "technic:coal_dust 2", "technic:silicon_wafer"},
{"technic:silicon_wafer", "technic:gold_dust", "technic:doped_silicon_wafer"},
-- from https://en.wikipedia.org/wiki/Carbon_black
-- The highest volume use of carbon black is as a reinforcing filler in rubber products, especially tires.
-- "[Compounding a] pure gum vulcanizate … with 50% of its weight of carbon black
-- improves its tensile strength and wear resistance …"
{"technic:raw_latex 4", "technic:coal_dust 2", "technic:rubber 6", 2},
{"technic:raw_latex 2", mat.coal_lump, "technic:rubber 2", 2},
{mat.ice, mat.bucket_empty, mat.bucket_water, 1 },
{mat.obsidian, mat.bucket_empty, mat.bucket_lava, 1 },
}
if minetest.get_modpath("ethereal") then
table.insert(recipes, {mat.clay, mat.dye_red, "bakedclay:red"})
table.insert(recipes, {mat.clay, mat.dye_orange, "bakedclay:orange"})
table.insert(recipes, {mat.clay, mat.dye_grey, "bakedclay:grey"})
end
if minetest.get_modpath("digilines") then
table.insert(recipes,
{"technic:lv_cable", "digilines:wire_std_00000000 2", "technic:lv_digi_cable", 18})
table.insert(recipes,
{"technic:lv_cable_plate_1", "digilines:wire_std_00000000 2", "technic:lv_digi_cable_plate_1", 18})
table.insert(recipes,
{"technic:mv_cable", "digilines:wire_std_00000000 2", "technic:mv_digi_cable", 18})
table.insert(recipes,
{"technic:mv_cable_plate_1", "digilines:wire_std_00000000 2", "technic:mv_digi_cable_plate_1", 18})
table.insert(recipes,
{"technic:hv_cable", "digilines:wire_std_00000000 2", "technic:hv_digi_cable", 18})
table.insert(recipes,
{"technic:hv_cable_plate_1", "digilines:wire_std_00000000 2", "technic:hv_digi_cable_plate_1", 18})
end
for _, data in pairs(recipes) do
technic.register_alloy_recipe({input = {data[1], data[2]}, output = data[3], time = data[4]})
end

View file

@ -0,0 +1,458 @@
local digilines_path = minetest.get_modpath("digilines")
local S = technic.getter
local tube_entry = "^pipeworks_tube_connection_metallic.png"
local cable_entry = "^technic_cable_connection_overlay.png"
local mat = technic.materials
-- Battery recipes:
-- Tin-copper recipe:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", mat.copper_ingot, "group:wood"},
{"group:wood", mat.tin_ingot, "group:wood"},
{"group:wood", mat.copper_ingot, "group:wood"},
}
})
-- Sulfur-lead-water recipes:
-- With sulfur lumps:
-- With water:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_lump", "group:wood"},
{"technic:lead_ingot", mat.bucket_water, "technic:lead_ingot"},
{"group:wood", "technic:sulfur_lump", "group:wood"},
},
replacements = {
{mat.bucket_water, mat.bucket_empty}
}
})
-- With oil extract:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_lump", "group:wood"},
{"technic:lead_ingot", "homedecor:oil_extract", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_lump", "group:wood"},
}
})
-- With sulfur dust:
-- With water:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_dust", "group:wood"},
{"technic:lead_ingot", mat.bucket_water, "technic:lead_ingot"},
{"group:wood", "technic:sulfur_dust", "group:wood"},
},
replacements = {
{mat.bucket_water, mat.bucket_empty}
}
})
-- With oil extract:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_dust", "group:wood"},
{"technic:lead_ingot", "homedecor:oil_extract", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_dust", "group:wood"},
}
})
technic.register_power_tool("technic:battery", {
description = S("RE Battery"),
inventory_image = "technic_battery.png",
groups = { disable_repair = 1 },
})
-- x+2 + (z+2)*2
local dirtab = {
[4] = 2,
[5] = 3,
[7] = 1,
[8] = 0
}
local tube = {
insert_object = function(pos, node, stack, direction)
if direction.y == 1
or (direction.y == 0 and dirtab[direction.x+2+(direction.z+2)*2] == node.param2) then
return stack
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if direction.y == 0 then
return inv:add_item("src", stack)
else
return inv:add_item("dst", stack)
end
end,
can_insert = function(pos, node, stack, direction)
if direction.y == 1
or (direction.y == 0 and dirtab[direction.x+2+(direction.z+2)*2] == node.param2) then
return false
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if direction.y == 0 then
return inv:room_for_item("src", stack)
else
return inv:room_for_item("dst", stack)
end
end,
connect_sides = {left=1, right=1, back=1, top=1},
}
function technic.register_battery_box(nodename, data)
local colon, modname, name, def = technic.register_compat_v1_to_v2(nodename, data, "battery_box")
local texture_prefix = modname.."_"..name
nodename = modname..":"..name
local tier = def.tier
local ltier = string.lower(tier)
local size = minetest.get_modpath("mcl_formspec") and "size[9,9]" or "size[8,9]"
local formspec =
size..
"image[1,1;1,2;technic_power_meter_bg.png]"..
"list[context;src;3,1;1,1;]"..
"image[4,1;1,1;technic_battery_reload.png]"..
"list[context;dst;5,1;1,1;]"..
"label[0,0;"..S("@1 Battery Box", S(tier)).."]"..
"label[3,0;"..S("Charge").."]"..
"label[5,0;"..S("Discharge").."]"..
"label[1,3;"..S("Power level").."]"..
(def.upgrade and
"list[context;upgrade1;3.5,3;1,1;]"..
"list[context;upgrade2;4.5,3;1,1;]"..
"label[3.5,4;"..S("Upgrade Slots").."]"
or "")
if minetest.get_modpath("mcl_formspec") then
formspec = formspec..
mcl_formspec.get_itemslot_bg(3,1,1,1)..
mcl_formspec.get_itemslot_bg(5,1,1,1)..
-- player inventory
"list[current_player;main;0,4.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,4.5,9,3)..
"list[current_player;main;0,7.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,7.74,9,1)..
-- upgrade
(def.upgrade and
mcl_formspec.get_itemslot_bg(3.5,3,1,1)..
mcl_formspec.get_itemslot_bg(4.5,3,1,1)
or "")
else
formspec = formspec..
"list[current_player;main;0,5;8,4;]"
end
-- listrings
formspec = formspec..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
(def.upgrade and
"listring[context;upgrade1]"..
"listring[current_player;main]"..
"listring[context;upgrade2]"..
"listring[current_player;main]"
or "")
--
-- Generate formspec with power meter
--
local function get_formspec(charge_ratio, channel)
return formspec .. "image[1,1;1,2;technic_power_meter_bg.png^[lowpart:" ..
math.floor(charge_ratio * 100) .. ":technic_power_meter_fg.png]" ..
(digilines_path and
("field[0.3,4;2.2,1;channel;"..S("Digiline Channel")..";${channel}]"..
"button[2,3.7;1,1;setchannel;"..S("Save").."]") or "")
end
--
-- Update fields not affecting internal network calculations and behavior in any way
--
local function update_node(pos, update_formspec)
-- Read metadata and calculate actual values based on upgrades
local meta = minetest.get_meta(pos)
local current_charge = meta:get_int("internal_EU_charge")
local EU_upgrade = 0
if def.upgrade then
EU_upgrade = technic.handle_machine_upgrades(meta)
end
local max_charge = def.max_charge * (1 + EU_upgrade / 10)
-- Select node textures
local charge_ratio = current_charge / max_charge
local charge_count = math.ceil(charge_ratio * 8)
charge_count = math.min(charge_count, 8)
charge_count = math.max(charge_count, 0)
local last_count = meta:get_float("last_side_shown")
if charge_count ~= last_count then
technic.swap_node(pos, nodename .. charge_count)
meta:set_float("last_side_shown", charge_count)
end
-- Update formspec and infotext
local eu_input = meta:get_int(tier.."_EU_input")
local infotext = S("@1 Battery Box: @2 / @3", tier,
technic.EU_string(current_charge), technic.EU_string(max_charge))
if eu_input == 0 then
infotext = S("@1 Idle", infotext)
end
meta:set_string("infotext", infotext)
if update_formspec then
local channel = meta:get_string("channel")
meta:set_string("formspec", get_formspec(charge_ratio, channel))
end
end
local function get_tool(inventory, listname)
-- Get itemstack and check if it is registered tool
if inventory:is_empty(listname) then
return
end
-- Get itemstack and check if it is registered tool
local toolstack = inventory:get_stack(listname, 1)
local tooldef = toolstack:get_definition()
if not tooldef.technic_max_charge then
return
end
return toolstack, tooldef
end
local function charge_tools(meta, batt_charge, charge_step)
-- Get tool metadata
local inv = meta:get_inventory()
local toolstack, tooldef = get_tool(inv, "src")
if not toolstack then
return batt_charge, false
end
-- Do the charging
local charge = tooldef.technic_get_charge(toolstack)
if charge >= tooldef.technic_max_charge then
return batt_charge, true
elseif batt_charge <= 0 then
return batt_charge, false
end
local oldcharge = charge
charge_step = math.min(charge_step, batt_charge, tooldef.technic_max_charge - charge)
charge = charge + charge_step
if charge ~= oldcharge then
tooldef.technic_set_charge(toolstack, charge)
inv:set_stack("src", 1, toolstack)
end
return batt_charge - charge_step, (charge == tooldef.technic_max_charge)
end
local function discharge_tools(meta, batt_charge, charge_step, batt_max_charge)
-- Get tool metadata
local inv = meta:get_inventory()
local toolstack, tooldef = get_tool(inv, "dst")
if not toolstack then
return batt_charge, false
end
-- Do the discharging
local charge = tooldef.technic_get_charge(toolstack)
if charge <= 0 then
return batt_charge, true
elseif batt_charge >= batt_max_charge then
return batt_charge, false
end
local oldcharge = charge
charge_step = math.min(charge_step, batt_max_charge - batt_charge, charge)
charge = charge - charge_step
if charge ~= oldcharge then
tooldef.technic_set_charge(toolstack, charge)
inv:set_stack("dst", 1, toolstack)
end
return batt_charge + charge_step, (charge == 0)
end
local function run(pos, node, run_state, network)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int(tier.."_EU_input")
local current_charge = meta:get_int("internal_EU_charge")
local EU_upgrade, tube_upgrade = 0, 0
if def.upgrade then
EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
end
local max_charge = def.max_charge * (1 + EU_upgrade / 10)
-- Charge/discharge the battery with the input EUs
if eu_input >= 0 then
current_charge = math.min(current_charge + eu_input, max_charge)
else
current_charge = math.max(current_charge + eu_input, 0)
end
-- Charging/discharging tools here
local tool_full, tool_empty
current_charge, tool_full = charge_tools(meta, current_charge, def.charge_step)
current_charge, tool_empty = discharge_tools(meta, current_charge, def.discharge_step, max_charge)
if def.tube and (tool_full or tool_empty) then
technic.handle_machine_pipeworks(pos, tube_upgrade, function(pos2, x_velocity, z_velocity)
if tool_full then
technic.send_items(pos2, x_velocity, z_velocity, "src")
elseif tool_empty then
technic.send_items(pos2, x_velocity, z_velocity, "dst")
end
end)
end
-- We allow batteries to charge on less than the demand
local supply = math.min(def.discharge_rate, current_charge)
local demand = math.min(def.charge_rate, max_charge - current_charge)
network:update_battery(current_charge, max_charge, supply, demand)
meta:set_int(tier.."_EU_demand", demand)
meta:set_int(tier.."_EU_supply", supply)
meta:set_int("internal_EU_charge", current_charge)
meta:set_int("internal_EU_charge_max", max_charge)
local timer = minetest.get_node_timer(pos)
if not timer:is_started() then
timer:start(2)
end
end
local function on_timer(pos, elapsed)
if not technic.pos2network(pos) then return end
update_node(pos)
return true
end
for i = 0, 8 do
local groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, ["technic_"..ltier]=1, axey=2, handy=1}
if i ~= 0 then
groups.not_in_creative_inventory = 1
end
if def.tube then
groups.tubedevice = 1
groups.tubedevice_receiver = 1
end
local top_tex = texture_prefix.."_top.png"..tube_entry
local front_tex = texture_prefix.."_front.png^technic_power_meter"..i..".png"
local side_tex = texture_prefix.."_side.png"..tube_entry
local bottom_tex = texture_prefix.."_bottom.png"..cable_entry
if ltier == "lv" then
top_tex = texture_prefix.."_top.png"
front_tex = texture_prefix.."_side.png^technic_power_meter"..i..".png"
side_tex = texture_prefix.."_side.png^technic_power_meter"..i..".png"
end
minetest.register_node(colon..nodename..i, {
description = S("@1 Battery Box", S(tier)),
tiles = {
top_tex,
bottom_tex,
side_tex,
side_tex,
side_tex,
front_tex},
groups = groups,
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom"},
tube = def.tube and tube or nil,
paramtype2 = "facedir",
sounds = technic.sounds.node_sound_wood_defaults(),
drop = "technic:"..ltier.."_battery_box0",
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Battery Box", S(tier)))
meta:set_int(tier.."_EU_demand", 0)
meta:set_int(tier.."_EU_supply", 0)
meta:set_int(tier.."_EU_input", 0)
meta:set_float("internal_EU_charge", 0)
local inv = meta:get_inventory()
inv:set_size("src", 1)
inv:set_size("dst", 1)
inv:set_size("upgrade1", 1)
inv:set_size("upgrade2", 1)
update_node(pos, true)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
technic_run = run,
on_timer = on_timer,
on_rightclick = function(pos) update_node(pos, true) end,
on_rotate = function(pos, node, user, mode, new_param2)
if mode ~= 1 then
return false
end
end,
after_place_node = def.tube and pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node,
on_receive_fields = function(pos, formname, fields, player)
if fields.quit then
return
end
local playername = player:get_player_name()
if minetest.is_protected(pos, playername) then
minetest.record_protection_violation(pos, playername)
return
elseif fields.setchannel then
local meta = minetest.get_meta(pos)
meta:set_string("channel", fields.channel or "")
update_node(pos, true)
end
end,
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if msg ~= "GET" and msg ~= "get" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local inv = meta:get_inventory()
digilines.receptor_send(pos, technic.digilines.rules, channel, {
demand = meta:get_int(tier.."_EU_demand"),
supply = meta:get_int(tier.."_EU_supply"),
input = meta:get_int(tier.."_EU_input"),
charge = meta:get_int("internal_EU_charge"),
max_charge = def.max_charge * (1 + technic.handle_machine_upgrades(meta) / 10),
src = inv:get_stack("src", 1):to_table(),
dst = inv:get_stack("dst", 1):to_table(),
upgrade1 = inv:get_stack("upgrade1", 1):to_table(),
upgrade2 = inv:get_stack("upgrade2", 1):to_table()
})
end
},
},
})
end
-- Register as a battery type
-- Battery type machines function as power reservoirs and can both receive and give back power
for i = 0, 8 do
technic.register_machine(tier, nodename..i, technic.battery)
end
end -- End registration

View file

@ -0,0 +1,162 @@
local cable_tier = {}
function technic.is_tier_cable(nodename, tier)
return cable_tier[nodename] == tier
end
function technic.get_cable_tier(nodename)
return cable_tier[nodename]
end
function technic.register_cable_tier(name, tier)
assert(technic.machines[tier], "Tier does not exist")
assert(type(name) == "string", "Invalid node name")
cable_tier[name] = tier
end
local function item_place_override_node(itemstack, placer, pointed, node)
-- Call the default on_place function with a fake itemstack
local temp_itemstack = ItemStack(itemstack)
temp_itemstack:set_name(node.name)
local original_count = temp_itemstack:get_count()
temp_itemstack =
minetest.item_place(temp_itemstack, placer, pointed, node.param2) or
temp_itemstack
-- Remove the same number of items from the real itemstack
itemstack:take_item(original_count - temp_itemstack:get_count())
return itemstack
end
local function cable_defaults(nodename, data)
assert(data.tier, "Technic cable registration requires `tier` field")
assert(data.size, "Technic cable registration requires `size` field")
assert(data.description, "Technic cable registration requires `description` field")
local def = table.copy(data)
local tier = def.tier
local ltier = string.lower(tier)
local size = def.size
local place_network_node = technic.place_network_node
local remove_network_node = technic.remove_network_node
def.connects_to = def.connects_to or {
"group:technic_"..ltier.."_cable",
"group:technic_"..ltier,
"group:technic_all_tiers"
}
def.groups = def.groups or {
snappy = 2,
choppy = 2,
oddly_breakable_by_hand = 2,
swordy = 1,
axey = 1,
handy = 1,
["technic_"..ltier.."_cable"] = 1
}
def.is_ground_content = false
def.drop = def.drop or nodename
def.sounds = def.sounds or technic.sounds.node_sound_wood_defaults()
def.on_construct = def.on_construct or function(pos) place_network_node(pos, {tier}, nodename) end
def.on_destruct = def.on_destruct or function(pos) remove_network_node(pos, {tier}, nodename) end
def.paramtype = def.paramtype or "light"
def.sunlight_propagates = not (def.sunlight_propagates == false and true)
def.drawtype = def.drawtype or "nodebox"
def.node_box = def.node_box or {
type = "connected",
fixed = {-size, -size, -size, size, size, size},
connect_top = {-size, -size, -size, size, 0.5, size}, -- y+
connect_bottom = {-size, -0.5, -size, size, size, size}, -- y-
connect_front = {-size, -size, -0.5, size, size, size}, -- z-
connect_back = {-size, -size, size, size, size, 0.5 }, -- z+
connect_left = {-0.5, -size, -size, size, size, size}, -- x-
connect_right = {-size, -size, -size, 0.5, size, size}, -- x+
}
return def
end
function technic.register_cable_plate(nodename, data)
local xyz = {"x","y","z"}
local notconnects = {"left", "bottom", "front", "right", "top", "back"}
local texture_basename = nodename:gsub(":", "_")
for i = 1, 6 do
-- Merge defaults and register cable plate
local def = cable_defaults(nodename.."_"..i, data)
local size = def.size
def.tiles = def.tiles or {texture_basename..".png"}
def.drop = nodename.."_1"
def.node_box.fixed = {
{-size, -size, -size, size, size, size},
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
}
def.node_box.fixed[1][i] = 7/16 * (i-3.5)/math.abs(i-3.5)
def.node_box.fixed[2][(i + 2) % 6 + 1] = 3/8 * (i-3.5)/math.abs(i-3.5)
def.node_box["connect_"..notconnects[i]] = nil
if i == 1 then
def.on_place = function(itemstack, placer, pointed_thing)
local count = 0
for axis in pairs(xyz) do
count = count + (pointed_thing.under[axis] == pointed_thing.above[axis] and 0 or 1)
if count > 1 then
return itemstack
end
end
local pointed_thing_diff = vector.direction(pointed_thing.under, pointed_thing.above)
local index = pointed_thing_diff.x + (pointed_thing_diff.y*2) + (pointed_thing_diff.z*3)
local num = index < 0 and -index + 3 or index
local crtl = placer:get_player_control()
if (crtl.aux1 or crtl.sneak) and not (crtl.aux1 and crtl.sneak) and index ~= 0 then
local fine_pointed = minetest.pointed_thing_to_face_pos(placer, pointed_thing)
fine_pointed = vector.direction(pointed_thing.above,fine_pointed)
fine_pointed[xyz[index < 0 and -index or index]] = nil
local key_a, a = next(fine_pointed)
local key_b, b = next(fine_pointed, key_a)
local far_key = math.abs(a) > math.abs(b) and key_a or key_b
local far = fine_pointed[far_key]
-- Plate facing
-- X pair floor +X 4 -X 1 -> Z pair, Y pair
-- Y pair floor +Y 5 -Y 2 -> X pair, Z pair
-- Z pair floor +Z 6 -Z 3 -> X pair, Y pair
if math.abs(far) < 0.3 then
num = num < 4 and num + 3 or num - 3
elseif far_key == "x" then
num = far < 0 and 1 or 4
elseif far_key == "y" then
num = far < 0 and 2 or 5
else
num = far < 0 and 3 or 6
end
end
local node = {name = nodename.."_"..(num ~= 0 and num or 1)}
return item_place_override_node(itemstack, placer, pointed_thing, node)
end
else
def.groups.not_in_creative_inventory = 1
def._mcl_blast_resistance = 1
def._mcl_hardness = 0.8
end
def.on_rotate = function(pos, node, user, mode, new_param2)
-- mode 1 is left-click, mode 2 is right-click
local dir = mode == 1 and 1 or (mode == 2 and -1 or 0)
local num = tonumber(node.name:sub(-1)) + dir - 1
minetest.swap_node(pos, {name = nodename.."_"..(num % 6 + 1)})
end
minetest.register_node(nodename.."_"..i, def)
cable_tier[nodename.."_"..i] = def.tier
end
end
function technic.register_cable(nodename, data)
-- Merge defaults and register cable
local def = cable_defaults(nodename, data)
local texture_basename = nodename:gsub(":", "_")
def.tiles = def.tiles or {texture_basename..".png"}
def.inventory_image = def.inventory_image or def.inventory_image ~= false and texture_basename.."_wield.png" or nil
def.wield_image = def.wield_image or def.wield_image ~= false and texture_basename.."_wield.png" or nil
def._mcl_blast_resistance = 1
def._mcl_hardness = 0.8
minetest.register_node(nodename, def)
cable_tier[nodename] = def.tier
end

View file

@ -0,0 +1,50 @@
local S = technic.getter
local mat = technic.materials
technic.register_recipe_type("separating", {
description = S("Separating"),
icon = "technic_mv_centrifuge_front.png",
output_size = 4,
})
function technic.register_separating_recipe(data)
data.time = data.time or 10
technic.register_recipe("separating", data)
end
local recipes = {
{ "technic:bronze_dust 8", "technic:copper_dust 7", "technic:tin_dust" },
{ "technic:stainless_steel_dust 5", "technic:wrought_iron_dust 4", "technic:chromium_dust" },
{ "technic:brass_dust 3", "technic:copper_dust 2", "technic:zinc_dust" },
{ "technic:chernobylite_dust", mat.sand, "technic:uranium3_dust" },
{ mat.dirt.." 4", mat.sand, mat.gravel, mat.clay_lump.." 4" },
}
local function uranium_dust(p)
return "technic:uranium"..(p == 7 and "" or p).."_dust"
end
for p = 1, 34 do
table.insert(recipes, { uranium_dust(p).." 2", uranium_dust(p-1), uranium_dust(p+1) })
end
if minetest.get_modpath("bushes_classic") then
for _, berry in ipairs({ "blackberry", "blueberry", "gooseberry", "raspberry", "strawberry" }) do
table.insert(recipes, { "bushes:"..berry.."_bush", mat.stick.." 20", "bushes:"..berry.." 4" })
end
end
if minetest.get_modpath("farming") or minetest.get_modpath("mcl_farming") then
if minetest.get_modpath("cottages") then
-- work as a mechanized threshing floor
table.insert(recipes, { "farming:wheat", "farming:seed_wheat", "cottages:straw_mat" })
table.insert(recipes, { "farming:barley", "farming:seed_barley", "cottages:straw_mat" })
else
-- work in a less fancy and less efficient manner
table.insert(recipes, { mat.wheat.." 4", mat.seed_wheat.." 3", mat.dry_shrub })
table.insert(recipes, { "farming:barley 4", "farming:seed_barley 3", mat.dry_shrub })
end
end
for _, data in pairs(recipes) do
technic.register_separating_recipe({ input = { data[1] }, output = { data[2], data[3], data[4] } })
end

View file

@ -0,0 +1,220 @@
local S = technic.getter
local mat = technic.materials
-- handles the machine upgrades every tick
function technic.handle_machine_upgrades(meta)
-- Get the names of the upgrades
local inv = meta:get_inventory()
local srcstack = inv:get_stack("upgrade1", 1)
local upg_item1 = srcstack and srcstack:get_name()
srcstack = inv:get_stack("upgrade2", 1)
local upg_item2 = srcstack and srcstack:get_name()
-- Save some power by installing battery upgrades.
-- Tube loading speed can be upgraded using control logic units.
local EU_upgrade = 0
local tube_upgrade = 0
if upg_item1 == "technic:control_logic_unit" then
tube_upgrade = tube_upgrade + 1
elseif upg_item1 == "technic:battery" then
EU_upgrade = EU_upgrade + 1
end
if upg_item2 == "technic:control_logic_unit" then
tube_upgrade = tube_upgrade + 1
elseif upg_item2 == "technic:battery" then
EU_upgrade = EU_upgrade + 1
end
return EU_upgrade, tube_upgrade
end
-- handles the machine upgrades when set or removed
local function on_machine_upgrade(meta, stack)
local stack_name = stack:get_name()
if stack_name == mat.chest then
meta:set_int("public", 1)
return 1
elseif stack_name ~= "technic:control_logic_unit"
and stack_name ~= "technic:battery" then
return 0
end
return 1
end
-- something is about to be removed
local function on_machine_downgrade(meta, stack, list)
if stack:get_name() == mat.chest then
local inv = meta:get_inventory()
local upg1, upg2 = inv:get_stack("upgrade1", 1), inv:get_stack("upgrade2", 1)
-- only set 0 if theres not a nother chest in the other list too
if (not upg1 or not upg2 or upg1:get_name() ~= upg2:get_name()) then
meta:set_int("public", 0)
end
end
return 1
end
function technic.send_items(pos, x_velocity, z_velocity, output_name)
-- Send items on their way in the pipe system.
if output_name == nil then
output_name = "dst"
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local i = 0
for _, stack in ipairs(inv:get_list(output_name)) do
i = i + 1
if stack then
local item0 = stack:to_table()
if item0 then
item0["count"] = 1
technic.tube_inject_item(pos, pos, vector.new(x_velocity, 0, z_velocity), item0)
stack:take_item(1)
inv:set_stack(output_name, i, stack)
return
end
end
end
end
function technic.handle_machine_pipeworks(pos, tube_upgrade, send_function)
if send_function == nil then
send_function = technic.send_items
end
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local pos1 = vector.new(pos)
local x_velocity = 0
local z_velocity = 0
-- Output is on the left side of the furnace
if node.param2 == 3 then pos1.z = pos1.z - 1 z_velocity = -1 end
if node.param2 == 2 then pos1.x = pos1.x - 1 x_velocity = -1 end
if node.param2 == 1 then pos1.z = pos1.z + 1 z_velocity = 1 end
if node.param2 == 0 then pos1.x = pos1.x + 1 x_velocity = 1 end
local output_tube_connected = false
local node1 = minetest.get_node(pos1)
if minetest.get_item_group(node1.name, "tubedevice") > 0 then
output_tube_connected = true
end
local tube_time = meta:get_int("tube_time") + tube_upgrade
if tube_time >= 2 then
tube_time = 0
if output_tube_connected then
send_function(pos, x_velocity, z_velocity)
end
end
meta:set_int("tube_time", tube_time)
end
function technic.machine_can_dig(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:is_empty("src") or not inv:is_empty("dst") or not inv:is_empty("fuel") then
if player then
minetest.chat_send_player(player:get_player_name(),
S("Machine cannot be removed because it is not empty"))
end
return false
end
return true
end
function technic.machine_after_dig_node(pos, oldnode, oldmetadata, player)
if oldmetadata.inventory then
if oldmetadata.inventory.upgrade1 and oldmetadata.inventory.upgrade1[1] then
local stack = ItemStack(oldmetadata.inventory.upgrade1[1])
if not stack:is_empty() then
minetest.add_item(pos, stack)
end
end
if oldmetadata.inventory.upgrade2 and oldmetadata.inventory.upgrade2[1] then
local stack = ItemStack(oldmetadata.inventory.upgrade2[1])
if not stack:is_empty() then
minetest.add_item(pos, stack)
end
end
end
if minetest.registered_nodes[oldnode.name].tube then
pipeworks.after_dig(pos, oldnode, oldmetadata, player)
end
end
local function inv_change(pos, player, count, from_list, to_list, stack)
local playername = player:get_player_name()
local meta = minetest.get_meta(pos);
local public = (meta:get_int("public") == 1)
local to_upgrade = to_list == "upgrade1" or to_list == "upgrade2"
local from_upgrade = from_list == "upgrade1" or from_list == "upgrade2"
if (not public or to_upgrade or from_upgrade) and minetest.is_protected(pos, playername) then
minetest.chat_send_player(playername, S("Inventory move disallowed due to protection"))
return 0
end
if to_upgrade then
-- only place a single item into it, if it's empty
local empty = meta:get_inventory():is_empty(to_list)
if empty then
return on_machine_upgrade(meta, stack)
end
return 0
elseif from_upgrade then
-- only called on take (not move)
on_machine_downgrade(meta, stack, from_list)
end
return count
end
function technic.machine_inventory_put(pos, listname, index, stack, player)
return inv_change(pos, player, stack:get_count(), nil, listname, stack)
end
function technic.machine_inventory_take(pos, listname, index, stack, player)
return inv_change(pos, player, stack:get_count(), listname, nil, stack)
end
function technic.machine_inventory_move(pos, from_list, from_index,
to_list, to_index, count, player)
local stack = minetest.get_meta(pos):get_inventory():get_stack(from_list, from_index)
return inv_change(pos, player, count, from_list, to_list, stack)
end
function technic.machine_on_inventory_put(pos, listname, index, stack, player)
minetest.log("action", string.format("%s puts %s into %s at %s",
player:get_player_name(),
stack:to_string(),
minetest.get_node(pos).name,
minetest.pos_to_string(pos)
))
end
function technic.machine_on_inventory_take(pos, listname, index, stack, player)
minetest.log("action", string.format("%s takes %s from %s at %s",
player:get_player_name(),
stack:to_string(),
minetest.get_node(pos).name,
minetest.pos_to_string(pos)
))
end
function technic.machine_on_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local stack = minetest.get_meta(pos):get_inventory():get_stack(to_list, to_index)
minetest.log("action", string.format("%s moves %s in %s at %s",
player:get_player_name(),
stack:to_string(),
minetest.get_node(pos).name,
minetest.pos_to_string(pos)
))
end

View file

@ -0,0 +1,61 @@
local S = technic.getter
local mat = technic.materials
local has_mcl = minetest.get_modpath("mcl_core")
technic.register_recipe_type("compressing", {
description = S("Compressing"),
icon = "technic_hv_compressor_front.png",
})
function technic.register_compressor_recipe(data)
data.time = data.time or 4
technic.register_recipe("compressing", data)
end
local recipes = {
{mat.snowblock, mat.ice},
{mat.sand.." 2", mat.sandstone},
{mat.desert_sand.." 2", mat.desert_sandstone},
{mat.silver_sand.." 2", mat.silver_sandstone},
{mat.desert_sandstone, mat.desert_stone},
{"technic:mixed_metal_ingot", "technic:composite_plate"},
{mat.copper_ingot.." 5", "technic:copper_plate"},
{"technic:coal_dust 4", "technic:graphite"},
{"technic:carbon_cloth", "technic:carbon_plate"},
{"technic:uranium35_ingot 5", "technic:uranium_fuel"},
{"technic:graphite 25", mat.diamond}
}
if minetest.get_modpath("ethereal") then
-- the density of charcoal is ~1/10 of coal, otherwise it's pure carbon
table.insert(recipes, {"ethereal:charcoal_lump 10", mat.coal_lump.." 1"})
end
-- defuse the default sandstone recipe, since we have the compressor to take over in a more realistic manner
if not has_mcl then
minetest.clear_craft({
recipe = {
{"default:sand", "default:sand"},
{"default:sand", "default:sand"},
},
})
minetest.clear_craft({
recipe = {
{"default:desert_sand", "default:desert_sand"},
{"default:desert_sand", "default:desert_sand"},
},
})
minetest.clear_craft({
recipe = {
{"default:silver_sand", "default:silver_sand"},
{"default:silver_sand", "default:silver_sand"},
},
})
end
for _, data in pairs(recipes) do
technic.register_compressor_recipe({input = {data[1]}, output = data[2]})
end

View file

@ -0,0 +1,136 @@
local S = technic.getter
technic.register_recipe_type("extracting", {
description = S("Extracting"),
icon = "technic_mv_extractor_front.png",
})
function technic.register_extractor_recipe(data)
data.time = data.time or 4
technic.register_recipe("extracting", data)
end
if minetest.get_modpath("dye") then
-- check if we are using dye or unifieddyes
local unifieddyes = minetest.get_modpath("unifieddyes")
-- register recipes with the same crafting ratios as `dye` provides
local dye_recipes = {
{"technic:coal_dust", "dye:black 2"},
{"default:blueberries", "dye:violet 2"},
{"default:grass_1", "dye:green 1"},
{"default:dry_shrub", "dye:brown 4"},
{"default:junglegrass", "dye:green 2"},
{"default:cactus", "dye:green 4"},
{"default:coral_green", "dye:green 4"},
{"default:coral_pink", "dye:pink 4"},
{"default:coral_cyan", "dye:cyan 4"},
{"default:coral_brown", "dye:brown 4"},
{"default:coral_orange", "dye:orange 4"},
{"default:coral_skeleton", "dye:white 8"},
{"flowers:chrysanthemum_green", "dye:green 4"},
{"flowers:geranium", "dye:blue 4"},
{"flowers:dandelion_white", "dye:white 4"},
{"flowers:dandelion_yellow", "dye:yellow 4"},
{"flowers:tulip", "dye:orange 4"},
{"flowers:tulip_black", "dye:black 8"},
{"flowers:rose", "dye:red 4"},
{"flowers:viola", "dye:violet 4"},
{"bushes:blackberry", unifieddyes and "unifieddyes:magenta_s50 4" or "dye:violet 4"},
{"bushes:blueberry", unifieddyes and "unifieddyes:magenta_s50 4" or "dye:magenta 4"},
}
if minetest.get_modpath("hunger") and minetest.get_modpath("ethereal") then
table.insert(dye_recipes, {"ethereal:willow_twig 12", "technic:aspirin_pill"})
end
if minetest.get_modpath("farming") then
-- Dyes ---
-- better recipes for farming's crafting methods (twice the output)
table.insert(dye_recipes, {"farming:chili_pepper", "dye:red 4"})
table.insert(dye_recipes, {"farming:beans", "dye:green 4"})
table.insert(dye_recipes, {"farming:grapes", "dye:violet 4"})
table.insert(dye_recipes, {"farming:cocoa_beans", "dye:brown 4"})
-- Some extra recipes:
table.insert(dye_recipes, {"farming:onion", "dye:yellow 4"})
table.insert(dye_recipes, {"farming:blueberries", "dye:blue 4"})
table.insert(dye_recipes, {"farming:raspberries", "dye:red 4"})
table.insert(dye_recipes, {"farming:blackberry", "dye:violet 4"})
-- Himalayan rhubarb root can give yellow dye IRL
table.insert(dye_recipes, {"farming:rhubarb", "dye:yellow 4"})
-- https://pubmed.ncbi.nlm.nih.gov/25401128
-- Biobleaching of industrial important dyes with peroxidase partially purified from garlic
table.insert(dye_recipes, {"farming:garlic", "dye:white 2"})
end
if minetest.get_modpath("ethereal") then
table.insert(dye_recipes, {"ethereal:seaweed", "dye:dark_green 6"})
table.insert(dye_recipes, {"ethereal:coral2", "dye:cyan 6"})
table.insert(dye_recipes, {"ethereal:coral3", "dye:orange 6"})
table.insert(dye_recipes, {"ethereal:coral4", "dye:pink 6"})
table.insert(dye_recipes, {"ethereal:coral5", "dye:green 6"})
table.insert(dye_recipes, {"ethereal:fern", "dye:dark_green 4"})
table.insert(dye_recipes, {"ethereal:snowygrass", "dye:grey 4"})
table.insert(dye_recipes, {"ethereal:crystalgrass", "dye:blue 4"})
end
if minetest.get_modpath("bakedclay") then
table.insert(dye_recipes, {"bakedclay:delphinium", "dye:cyan 8"})
table.insert(dye_recipes, {"bakedclay:thistle", "dye:magenta 8"})
table.insert(dye_recipes, {"bakedclay:lazarus", "dye:pink 8"})
table.insert(dye_recipes, {"bakedclay:mannagrass", "dye:dark_green 8"})
end
if minetest.get_modpath("bonemeal") then
table.insert(dye_recipes, {"bonemeal:bone", "dye:white 8"})
table.insert(dye_recipes, {"bonemeal:bonemeal", "dye:white 4"})
end
for _, data in ipairs(dye_recipes) do
technic.register_extractor_recipe({input = {data[1]}, output = data[2]})
end
-- overwrite the existing crafting recipes
local dyes = {"white", "red", "yellow", "blue", "violet", "orange"}
for _, color in ipairs(dyes) do
minetest.clear_craft({
recipe = {
{"group:flower,color_"..color}
},
})
minetest.register_craft({
output = "dye:"..color.." 1",
recipe = {
{"group:flower,color_"..color}
},
})
end
minetest.clear_craft({
recipe = {
{"group:coal"}
},
})
minetest.register_craft({
output = "dye:black 1",
recipe = {
{"group:coal"}
},
})
if unifieddyes then
minetest.clear_craft({
recipe = {
{"default:cactus"}
},
})
minetest.register_craft({
output = "dye:green 1",
recipe = {
{"default:cactus"}
},
})
end
end

View file

@ -0,0 +1,25 @@
local S = technic.getter
local mat = technic.materials
technic.register_recipe_type("freezing", {
description = S("Freezing"),
icon = "technic_mv_freezer_front.png",
})
function technic.register_freezer_recipe(data)
data.time = data.time or 5
technic.register_recipe("freezing", data)
end
local recipes = {
{mat.bucket_water, { mat.ice, mat.bucket_empty } },
{mat.bucket_river_water, { mat.ice, mat.bucket_empty } },
{mat.dirt, mat.dirt_with_snow },
{mat.bucket_lava, { mat.obsidian, mat.bucket_empty } }
}
for _, data in pairs(recipes) do
technic.register_freezer_recipe({input = {data[1]}, output = data[2], hidden = true})
end

View file

@ -0,0 +1,294 @@
local S = technic.getter
local fs_helpers = pipeworks.fs_helpers
local tube_entry = "^pipeworks_tube_connection_metallic.png"
local tube = {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("src", stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("src", stack)
end,
connect_sides = {left=1, right=1, back=1, top=1, bottom=1},
}
local size = minetest.get_modpath("mcl_formspec") and "size[9,10]" or "size[8,9]"
local function update_generator_formspec(meta, desc, percent, form_buttons)
local generator_formspec = size..
"label[0, 0;"..desc.."]"..
"list[context;src;3,1;1,1;]"..
"listring[context;src]"..
"image[4,1;1,1;default_furnace_fire_bg.png^[lowpart:"..
(percent)..":default_furnace_fire_fg.png]"..
form_buttons
if minetest.get_modpath("mcl_formspec") then
generator_formspec = generator_formspec..
mcl_formspec.get_itemslot_bg(3,1,1,1)..
-- player inventory
"list[current_player;main;0,5.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5.5,9,3)..
"list[current_player;main;0,8.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.74,9,1)..
"listring[current_player;main]"
else
generator_formspec = generator_formspec..
"list[current_player;main;0, 5;8, 4;]"..
"listring[current_player;main]"
end
return meta:set_string("formspec", generator_formspec)
end
function technic.register_generator(data)
local tier = data.tier
local ltier = string.lower(tier)
local groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, ["technic_"..ltier]=1, axey=2, handy=1}
if data.tube then
groups.tubedevice = 1
groups.tubedevice_receiver = 1
end
local active_groups = {not_in_creative_inventory = 1}
for k, v in pairs(groups) do active_groups[k] = v end
local desc = S("Fuel-Fired @1 Generator", S(tier))
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local burn_time = meta:get_int("burn_time")
local burn_totaltime = meta:get_int("burn_totaltime")
-- If more to burn and the energy produced was used: produce some more
if burn_time > 0 then
meta:set_int(tier.."_EU_supply", data.supply)
burn_time = burn_time - 1
meta:set_int("burn_time", burn_time)
end
-- Burn another piece of fuel
if burn_time == 0 then
local inv = meta:get_inventory()
if not inv:is_empty("src") then
local fuellist = inv:get_list("src")
local fuel
local afterfuel
fuel, afterfuel = minetest.get_craft_result(
{method = "fuel", width = 1,
items = fuellist})
if not fuel or fuel.time == 0 then
meta:set_string("infotext", S("@1 Out Of Fuel", desc))
technic.swap_node(pos, "technic:"..ltier.."_generator")
meta:set_int(tier.."_EU_supply", 0)
return
end
meta:set_int("burn_time", fuel.time)
meta:set_int("burn_totaltime", fuel.time)
inv:set_stack("src", 1, afterfuel.items[1])
technic.swap_node(pos, "technic:"..ltier.."_generator_active")
meta:set_int(tier.."_EU_supply", data.supply)
else
technic.swap_node(pos, "technic:"..ltier.."_generator")
meta:set_int(tier.."_EU_supply", 0)
end
end
if burn_totaltime == 0 then burn_totaltime = 1 end
local percent = math.floor((burn_time / burn_totaltime) * 100)
meta:set_string("infotext", desc.." ("..percent.."%)")
local form_buttons = ""
if ltier ~= "lv" then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
update_generator_formspec(meta, desc, percent, form_buttons)
end
local tentry = tube_entry
if ltier == "lv" then tentry = "" end
minetest.register_node("technic:"..ltier.."_generator", {
description = desc,
tiles = {
"technic_"..ltier.."_generator_top.png"..tentry,
"technic_machine_bottom.png"..tentry,
"technic_"..ltier.."_generator_side.png"..tentry,
"technic_"..ltier.."_generator_side.png"..tentry,
"technic_"..ltier.."_generator_side.png"..tentry,
"technic_"..ltier.."_generator_front.png"
},
paramtype2 = "facedir",
groups = groups,
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom", "back", "left", "right"},
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
tube = data.tube and tube or nil,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
meta:set_string("infotext", desc)
meta:set_int(data.tier.."_EU_supply", 0)
meta:set_int("burn_time", 0)
meta:set_int("tube_time", 0)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
update_generator_formspec(meta, desc, 0, form_buttons)
local inv = meta:get_inventory()
inv:set_size("src", 1)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
technic_run = run,
after_place_node = data.tube and pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node,
on_receive_fields = function(pos, formname, fields, sender)
if not pipeworks.may_configure(pos, sender) then return end
fs_helpers.on_receive_fields(pos, fields)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
local burn_totaltime = meta:get_int("burn_totaltime") or 0
local burn_time = meta:get_int("burn_time")
local percent = math.floor(burn_time / burn_totaltime * 100)
update_generator_formspec(meta, desc, percent, form_buttons)
end,
})
minetest.register_node("technic:"..ltier.."_generator_active", {
description = desc,
tiles = {
"technic_"..ltier.."_generator_top.png"..tube_entry,
"technic_machine_bottom.png"..tube_entry,
"technic_"..ltier.."_generator_side.png"..tube_entry,
"technic_"..ltier.."_generator_side.png"..tube_entry,
"technic_"..ltier.."_generator_side.png"..tube_entry,
"technic_"..ltier.."_generator_front_active.png"
},
paramtype2 = "facedir",
groups = active_groups,
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom"},
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
tube = data.tube and tube or nil,
drop = "technic:"..ltier.."_generator",
can_dig = technic.machine_can_dig,
after_dig_node = technic.machine_after_dig_node,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
technic_run = run,
technic_on_disable = function(pos, node)
local timer = minetest.get_node_timer(pos)
timer:start(1)
end,
on_timer = function(pos)
-- Connected back?
if technic.get_timeout(tier, pos) > 0 then return false end
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local burn_time = meta:get_int("burn_time") or 0
if burn_time <= 0 then
meta:set_int(tier.."_EU_supply", 0)
meta:set_int("burn_time", 0)
technic.swap_node(pos, "technic:"..ltier.."_generator")
return false
end
local burn_totaltime = meta:get_int("burn_totaltime") or 0
if burn_totaltime == 0 then burn_totaltime = 1 end
burn_time = burn_time - 1
meta:set_int("burn_time", burn_time)
local percent = math.floor(burn_time / burn_totaltime * 100)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
update_generator_formspec(meta, desc, percent, form_buttons)
return true
end,
on_receive_fields = function(pos, formname, fields, sender)
if not pipeworks.may_configure(pos, sender) then return end
fs_helpers.on_receive_fields(pos, fields)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
local burn_totaltime = meta:get_int("burn_totaltime") or 0
local burn_time = meta:get_int("burn_time")
local percent = math.floor(burn_time / burn_totaltime * 100)
update_generator_formspec(meta, desc, percent, form_buttons)
end,
})
technic.register_machine(tier, "technic:"..ltier.."_generator", technic.producer)
technic.register_machine(tier, "technic:"..ltier.."_generator_active", technic.producer)
end

View file

@ -0,0 +1,182 @@
local S = technic.getter
local mat = technic.materials
technic.register_recipe_type("grinding", {
description = S("Grinding"),
icon = "technic_hv_grinder_front.png",
})
function technic.register_grinder_recipe(data)
data.time = data.time or 3
technic.register_recipe("grinding", data)
end
local recipes = {
-- Dusts
{mat.coal_lump, "technic:coal_dust 2"},
{mat.copper_lump, "technic:copper_dust 2"},
{mat.desert_stone, mat.desert_sand},
{mat.gold_lump, "technic:gold_dust 2"},
{mat.iron_lump, "technic:wrought_iron_dust 2"},
{mat.tin_lump, "technic:tin_dust 2"},
{"technic:chromium_lump", "technic:chromium_dust 2"},
{"technic:uranium_lump", "technic:uranium_dust 2"},
{"technic:zinc_lump", "technic:zinc_dust 2"},
{"technic:lead_lump", "technic:lead_dust 2"},
{"technic:sulfur_lump", "technic:sulfur_dust 2"},
{mat.stone, "technic:stone_dust"},
{mat.sand, "technic:stone_dust"},
{mat.desert_sand, "technic:stone_dust"},
{mat.silver_sand, "technic:stone_dust"},
-- Other
{mat.cobble, mat.gravel},
{mat.gravel, mat.sand},
{mat.sandstone, mat.sand.." 2"}, -- reverse recipe can be found in the compressor
{mat.desert_sandstone, mat.desert_sand.." 2"}, -- reverse recipe can be found in the compressor
{mat.silver_sandstone, mat.silver_sand.." 2"}, -- reverse recipe can be found in the compressor
{mat.ice, mat.snowblock},
}
if minetest.get_modpath("ethereal") then
-- the density of charcoal is ~1/10 of coal, otherwise it's the same graphitic carbon
table.insert(recipes, {"ethereal:charcoal_lump 5", "technic:coal_dust 1"})
end
-- defuse the sandstone -> 4 sand recipe to avoid infinite sand bugs (also consult the inverse compressor recipe)
minetest.clear_craft({
recipe = {
{mat.sandstone}
},
})
minetest.clear_craft({
recipe = {
{mat.desert_sandstone}
},
})
minetest.clear_craft({
recipe = {
{mat.silver_sandstone}
},
})
if minetest.get_modpath("farming") then
table.insert(recipes, {mat.seed_wheat, "farming:flour 1"})
end
if minetest.get_modpath("moreores") then
table.insert(recipes, {"moreores:mithril_lump", "technic:mithril_dust 2"})
table.insert(recipes, {"moreores:silver_lump", "technic:silver_dust 2"})
end
if minetest.get_modpath("gloopores") or minetest.get_modpath("glooptest") then
table.insert(recipes, {"gloopores:alatro_lump", "technic:alatro_dust 2"})
table.insert(recipes, {"gloopores:kalite_lump", "technic:kalite_dust 2"})
table.insert(recipes, {"gloopores:arol_lump", "technic:arol_dust 2"})
table.insert(recipes, {"gloopores:talinite_lump", "technic:talinite_dust 2"})
table.insert(recipes, {"gloopores:akalin_lump", "technic:akalin_dust 2"})
end
if minetest.get_modpath("homedecor") then
table.insert(recipes, {"home_decor:brass_ingot", "technic:brass_dust 1"})
end
for _, data in pairs(recipes) do
technic.register_grinder_recipe({input = {data[1]}, output = data[2]})
end
-- dusts
local function register_dust(name, ingot)
local lname = string.lower(name)
lname = string.gsub(lname, ' ', '_')
minetest.register_craftitem("technic:"..lname.."_dust", {
description = S("@1 Dust", S(name)),
inventory_image = "technic_"..lname.."_dust.png",
})
if ingot then
minetest.register_craft({
type = "cooking",
recipe = "technic:"..lname.."_dust",
output = ingot,
})
technic.register_grinder_recipe({ input = {ingot}, output = "technic:"..lname.."_dust 1" })
end
end
-- Sorted alphibeticaly
register_dust("Brass", "basic_materials:brass_ingot")
register_dust("Bronze", mat.bronze_ingot)
register_dust("Carbon Steel", "technic:carbon_steel_ingot")
register_dust("Cast Iron", "technic:cast_iron_ingot")
register_dust("Chernobylite", "technic:chernobylite_block")
register_dust("Chromium", "technic:chromium_ingot")
register_dust("Coal", nil)
register_dust("Copper", mat.copper_ingot)
register_dust("Lead", "technic:lead_ingot")
register_dust("Gold", mat.gold_ingot)
register_dust("Mithril", mat.mithril_ingot)
register_dust("Silver", mat.silver_ingot)
register_dust("Stainless Steel", "technic:stainless_steel_ingot")
register_dust("Stone", mat.stone)
register_dust("Sulfur", nil)
register_dust("Tin", mat.tin_ingot)
register_dust("Wrought Iron", "technic:wrought_iron_ingot")
register_dust("Zinc", "technic:zinc_ingot")
if minetest.get_modpath("gloopores") or minetest.get_modpath("glooptest") then
register_dust("Akalin", "glooptest:akalin_ingot")
register_dust("Alatro", "glooptest:alatro_ingot")
register_dust("Arol", "glooptest:arol_ingot")
register_dust("Kalite", nil)
register_dust("Talinite", "glooptest:talinite_ingot")
end
for p = 0, 35 do
local nici = (p ~= 0 and p ~= 7 and p ~= 35) and 1 or nil
local psuffix = p == 7 and "" or p
local ingot = "technic:uranium"..psuffix.."_ingot"
local dust = "technic:uranium"..psuffix.."_dust"
minetest.register_craftitem(dust, {
description = S("@1%-Fissile Uranium Dust", string.format("%.1f", p/10)),
inventory_image = "technic_uranium_dust.png",
on_place_on_ground = minetest.craftitem_place_item,
groups = {uranium_dust=1, not_in_creative_inventory=nici},
})
minetest.register_craft({
type = "cooking",
recipe = dust,
output = ingot,
})
technic.register_grinder_recipe({ input = {ingot}, output = dust })
end
local function uranium_dust(p)
return "technic:uranium"..(p == 7 and "" or p).."_dust"
end
for pa = 0, 34 do
for pb = pa+1, 35 do
local pc = (pa+pb)/2
if pc == math.floor(pc) then
minetest.register_craft({
type = "shapeless",
recipe = { uranium_dust(pa), uranium_dust(pb) },
output = uranium_dust(pc).." 2",
})
end
end
end
minetest.register_craft({
type = "fuel",
recipe = "technic:coal_dust",
burntime = 50,
})
if minetest.get_modpath("gloopores") or minetest.get_modpath("glooptest") then
minetest.register_craft({
type = "fuel",
recipe = "technic:kalite_dust",
burntime = 37.5,
})
end

View file

@ -0,0 +1,60 @@
local S = technic.getter
local moretrees = minetest.get_modpath("moretrees")
local dye = minetest.get_modpath("dye")
local mat = technic.materials
-- sawdust, the finest wood/tree grinding
local sawdust = "technic:sawdust"
minetest.register_craftitem(sawdust, {
description = S("Sawdust"),
inventory_image = "technic_sawdust.png",
})
minetest.register_craft({ type = "fuel", recipe = sawdust, burntime = 6 })
technic.register_compressor_recipe({ input = {sawdust .. " 4"}, output = mat.wood })
-- tree/wood grindings
local function register_tree_grinding(name, tree, wood, extract, grinding_color)
local lname = string.lower(name)
lname = string.gsub(lname, ' ', '_')
local grindings_name = "technic:"..lname.."_grindings"
if not minetest.registered_craftitems[grindings_name] then
local inventory_image = "technic_"..lname.."_grindings.png"
if grinding_color then
inventory_image = inventory_image .. "^[colorize:" .. grinding_color
end
minetest.register_craftitem(grindings_name, {
description = S("@1 Grinding", S(name)),
inventory_image = inventory_image,
})
minetest.register_craft({
type = "fuel",
recipe = grindings_name,
burntime = 8,
})
end
technic.register_grinder_recipe({ input = { tree }, output = grindings_name .. " 4" })
technic.register_grinder_recipe({ input = { grindings_name }, output = sawdust .. " 4" })
if wood then
technic.register_grinder_recipe({ input = { wood }, output = grindings_name })
end
if extract then
technic.register_extractor_recipe({ input = { grindings_name .. " 4" }, output = extract})
technic.register_separating_recipe({
input = { grindings_name .. " 4" },
output = { sawdust .. " 4", extract }
})
end
end
local rubber_planks = moretrees and "moretrees:rubber_tree_planks"
local default_extract = dye and "dye:brown 2"
-- https://en.wikipedia.org/wiki/Catechu ancient brown dye from the wood of acacia trees
local acacia_extract = dye and "dye:brown 8"
-- Specific recipes for acacia and rubber trees
register_tree_grinding("Acacia", mat.acacia_tree, mat.acacia_wood, acacia_extract)
register_tree_grinding("Rubber Tree", "moretrees:rubber_tree_trunk", rubber_planks, "technic:raw_latex 2")
register_tree_grinding("Rubber Tree", "moretrees:rubber_tree_trunk_empty", nil, "technic:raw_latex")
-- Group recipe for all other trees
register_tree_grinding("Common Tree", "group:tree", "group:wood", default_extract)

View file

@ -0,0 +1,26 @@
local path = technic.modpath.."/machines/register"
dofile(path.."/common.lua")
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
dofile(path.."/solar_array.lua")
dofile(path.."/generator.lua")
-- API for machines
dofile(path.."/recipes.lua")
dofile(path.."/machine_base.lua")
-- Recipes
dofile(path.."/alloy_recipes.lua")
dofile(path.."/grinder_recipes.lua")
dofile(path.."/extractor_recipes.lua")
dofile(path.."/compressor_recipes.lua")
dofile(path.."/centrifuge_recipes.lua")
dofile(path.."/freezer_recipes.lua")
-- Multi-Machine Recipes
dofile(path.."/grindings.lua")

View file

@ -0,0 +1,299 @@
local S = technic.getter
local fs_helpers = pipeworks.fs_helpers
local tube_entry = "^pipeworks_tube_connection_metallic.png"
function technic.default_can_insert(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return inv:room_for_item("src", stack)
end
function technic.new_default_tube()
return {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("src", stack)
end,
can_insert = technic.default_can_insert,
connect_sides = {left = 1, right = 1, back = 1, top = 1, bottom = 1},
}
end
local connect_default = {"bottom", "back", "left", "right"}
function technic.register_base_machine(nodename, data)
local colon, modname, name, def = technic.register_compat_v1_to_v2(nodename, data)
local texture_prefix = modname.."_"..name
nodename = modname..":"..name
local typename = def.typename
local input_size = technic.recipes[typename].input_size
local tier = def.tier
local ltier = string.lower(tier)
local infotext_idle = S("@1 Idle", def.description)
local infotext_active = S("@1 Active", def.description)
local infotext_unpowered = S("@1 Unpowered", def.description)
local groups = {cracky = 2, technic_machine = 1, ["technic_"..ltier] = 1, pickaxey=2}
if def.tube then
groups.tubedevice = 1
groups.tubedevice_receiver = 1
end
local active_groups = table.copy(groups)
active_groups.not_in_creative_inventory = 1
local size = minetest.get_modpath("mcl_formspec") and "size[9,10]" or "size[8,9]"
local formspec =
size..
"list[context;src;"..(4-input_size)..",1;"..input_size..",1;]"..
"list[context;dst;5,1;2,2;]"..
"label[0,0;"..def.description.."]"
if def.upgrade then
formspec = formspec..
"list[context;upgrade1;1,3;1,1;]"..
"list[context;upgrade2;2,3;1,1;]"..
"label[1,4;"..S("Upgrade Slots").."]"
end
if minetest.get_modpath("mcl_formspec") then
formspec = formspec..
mcl_formspec.get_itemslot_bg(4-input_size,1,input_size,1)..
mcl_formspec.get_itemslot_bg(5,1,2,2)..
-- player inventory
"list[current_player;main;0,5.5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5.5,9,3)..
"list[current_player;main;0,8.74;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.74,9,1)
if def.upgrade then
formspec = formspec..
mcl_formspec.get_itemslot_bg(1,3,1,1)..
mcl_formspec.get_itemslot_bg(2,3,1,1)
end
else
formspec = formspec..
"list[current_player;main;0,5;8,4;]"
end
-- listrings
formspec = formspec..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"
if def.upgrade then
formspec = formspec..
"listring[context;upgrade1]"..
"listring[current_player;main]"..
"listring[context;upgrade2]"..
"listring[current_player;main]"
end
local tube = technic.new_default_tube()
if def.can_insert then
tube.can_insert = def.can_insert
end
if def.insert_object then
tube.insert_object = def.insert_object
end
local update_node = function(pos, meta, newnode, infotext, demand, src_time)
technic.swap_node(pos, newnode)
meta:set_string("infotext", infotext)
meta:set_int(tier.."_EU_demand", demand)
meta:set_int("src_time", src_time)
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int(tier.."_EU_input")
local machine_demand = def.demand
-- Setup meta def if it does not exist.
if not eu_input then
meta:set_int(tier.."_EU_demand", machine_demand[1])
meta:set_int(tier.."_EU_input", 0)
return
end
local EU_upgrade, tube_upgrade = 0, 0
if def.upgrade then
EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
end
if def.tube then
technic.handle_machine_pipeworks(pos, tube_upgrade)
end
local inv = meta:get_inventory()
local demand = machine_demand[EU_upgrade+1]
local powered = eu_input >= demand
local src_time = meta:get_int("src_time")
if powered then
src_time = src_time + math.floor(def.speed * 10 + 0.5)
end
while true do
local recipe = inv:get_list("src") and technic.get_recipe(typename, inv:get_list("src"))
if not recipe then
update_node(pos, meta, nodename, infotext_idle, 0, 0)
return
end
local recipe_time = math.floor(recipe.time * 10 + 0.5)
if src_time < recipe_time then
if powered then
local infotext = infotext_active .. "\n" .. S("Demand: @1", technic.EU_string(demand))
update_node(pos, meta, nodename.."_active", infotext, demand, src_time)
else
update_node(pos, meta, nodename, infotext_unpowered, demand, src_time)
end
return
elseif not technic.process_recipe(recipe, inv) then
update_node(pos, meta, nodename, infotext_idle, 0, recipe_time)
return
end
src_time = src_time - recipe_time
end
end
local tentry = tube_entry
if ltier == "lv" then
tentry = ""
end
minetest.register_node(colon..nodename, {
description = def.description,
tiles = {
texture_prefix.."_top.png"..tentry,
texture_prefix.."_bottom.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_front.png"
},
paramtype2 = "facedir",
groups = groups,
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
tube = def.tube and tube or nil,
connect_sides = def.connect_sides or connect_default,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
meta:set_string("infotext", def.description)
meta:set_int("tube_time", 0)
meta:set_string("formspec", formspec..form_buttons)
local inv = meta:get_inventory()
inv:set_size("src", input_size)
inv:set_size("dst", 4)
inv:set_size("upgrade1", 1)
inv:set_size("upgrade2", 1)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
technic_run = run,
after_place_node = def.tube and pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node,
on_receive_fields = function(pos, formname, fields, sender)
if fields.quit then return end
if not pipeworks.may_configure(pos, sender) then return end
fs_helpers.on_receive_fields(pos, fields)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
meta:set_string("formspec", formspec..form_buttons)
end,
})
minetest.register_node(colon..nodename.."_active",{
description = def.description,
tiles = {
texture_prefix.."_top.png"..tentry,
texture_prefix.."_bottom.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_side.png"..tentry,
texture_prefix.."_front_active.png"
},
paramtype2 = "facedir",
drop = nodename,
groups = active_groups,
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = def.connect_sides or connect_default,
legacy_facedir_simple = true,
sounds = technic.sounds.node_sound_wood_defaults(),
tube = def.tube and tube or nil,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_metadata_inventory_move = technic.machine_on_inventory_move,
on_metadata_inventory_put = technic.machine_on_inventory_put,
on_metadata_inventory_take = technic.machine_on_inventory_take,
technic_run = run,
technic_disabled_machine_name = nodename,
on_receive_fields = function(pos, formname, fields, sender)
if fields.quit then return end
if not pipeworks.may_configure(pos, sender) then return end
fs_helpers.on_receive_fields(pos, fields)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local form_buttons = ""
if not string.find(node.name, ":lv_") then
form_buttons = fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
end
meta:set_string("formspec", formspec..form_buttons)
end,
})
technic.register_machine(tier, nodename, technic.receiver)
technic.register_machine(tier, nodename.."_active", technic.receiver)
end -- End registration

View file

@ -0,0 +1,263 @@
local have_ui = minetest.get_modpath("unified_inventory")
local have_cg = minetest.get_modpath("craftguide")
local have_mcl_cg = minetest.get_modpath("mcl_craftguide")
local have_i3 = minetest.get_modpath("i3")
technic.recipes = {
cooking = {input_size = 1, output_size = 1, recipes = {}},
}
local temp_recipes = {} -- Used to store recipes before caching
local recipe_cache = {} -- Cache used by technic.get_recipe
function technic.register_recipe_type(method, data)
data = table.copy(data)
data.input_size = data.input_size or 1
data.output_size = data.output_size or 1
data.recipes = {}
if have_ui then
unified_inventory.register_craft_type(method, {
description = data.description,
icon = data.icon,
width = data.input_size,
height = 1,
})
end
if have_cg then
craftguide.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
if have_mcl_cg then
mcl_craftguide.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
if have_i3 then
i3.register_craft_type(method, {
description = data.description,
icon = data.icon,
})
end
technic.recipes[method] = data
end
function technic.register_recipe(method, data)
data.time = data.time or 1
data.method = method
if type(data.input) == "string" then
data.input = {data.input}
end
if type(data.output) == "string" then
data.output = {data.output}
end
table.insert(temp_recipes, data)
end
local function get_recipe_key(method, items)
local t = {}
for i, stack in ipairs(items) do
t[i] = ItemStack(stack):get_name()
end
table.sort(t)
return method.."/"..table.concat(t, "/")
end
function technic.get_recipe(method, items)
local key = get_recipe_key(method, items)
local recipe = recipe_cache[key]
if not recipe then
return
end
local new_input = {}
for i, stack in ipairs(items) do
local amount = recipe.input[stack:get_name()]
if stack:get_count() < amount then
return
else
new_input[i] = ItemStack(stack)
new_input[i]:take_item(amount)
end
end
return {
time = recipe.time,
new_input = new_input,
output = recipe.output
}
end
local function add_to_craftguides(recipe)
for _, output in ipairs(recipe.output) do
if have_ui then
unified_inventory.register_craft({
type = recipe.method,
output = output,
items = table.copy(recipe.input),
width = 0,
})
end
if have_cg and craftguide.register_craft then
craftguide.register_craft({
type = recipe.method,
result = output,
items = {table.concat(recipe.input, ", ")},
})
end
if have_mcl_cg then
mcl_craftguide.register_craft({
type = recipe.method,
output = output,
items = table.copy(recipe.input),
width = 0,
})
end
if have_i3 then
i3.register_craft({
type = recipe.method,
result = output,
items = {table.concat(recipe.input, ", ")},
})
end
end
end
local function get_items_in_group(group)
local items = {}
local groups = group:split(",")
for name, def in pairs(minetest.registered_items) do
local match = true
for _,g in pairs(groups) do
if not def.groups[g] then
match = false
break
end
end
if match then
items[#items+1] = name
end
end
return items
end
local function get_recipe_variants(items, index)
index = index or 1
if not items[index] then
return
end
local list = {}
local variants = get_recipe_variants(items, index + 1)
if variants then
for _,a in pairs(items[index]) do
for _,b in pairs(variants) do
list[#list+1] = a..","..b
end
end
else
for _,a in pairs(items[index]) do
list[#list+1] = a
end
end
if index == 1 then
for i, str in pairs(list) do
list[i] = str:split(",")
end
end
return list
end
local function cache_recipe(data)
-- Create the basic recipe
local recipe = {time = data.time, input = {}, output = {}}
for _, item in ipairs(data.input) do
if item:match("^group:") then
local split = item:split(" ")
recipe.input[split[1]] = tonumber(split[2]) or 1
else
local stack = ItemStack(item)
recipe.input[stack:get_name()] = stack:get_count()
end
end
for i, item in ipairs(data.output) do
recipe.output[i] = ItemStack(item):to_string()
end
if data.method ~= "cooking" then
table.insert(technic.recipes[data.method].recipes, recipe)
end
-- Find all unique variants of the recipe and cache them
-- If there are no group items, there will only be one
local all_items, item_counts = {}, {}
local has_group_item = false
for item, count in pairs(recipe.input) do
local group = item:match("^group:(.+)$")
if group then
table.insert(all_items, get_items_in_group(group))
has_group_item = true
else
table.insert(all_items, {ItemStack(item):get_name()})
end
table.insert(item_counts, count)
end
if not has_group_item then
local key = get_recipe_key(data.method, data.input)
recipe_cache[key] = table.copy(recipe)
return
end
for _,items in pairs(get_recipe_variants(all_items)) do
local key = get_recipe_key(data.method, items)
-- Non-group recipes take priority over group recipes
if not has_group_item or not recipe_cache[key] then
local input = {}
for i, item in ipairs(items) do
input[item] = item_counts[i]
end
recipe_cache[key] = {
time = data.time,
input = input,
output = table.copy(recipe.output),
}
end
end
end
local function cache_all_recipes()
-- Cache built in cooking recipes
for item in pairs(minetest.registered_items) do
local recipes = minetest.get_all_craft_recipes(item)
for _,recipe in ipairs(recipes or {}) do
if recipe.method == "cooking" then
local result, new_input = minetest.get_craft_result(recipe)
if result and result.time > 0 then
local data = {
method = "cooking",
time = result.time,
input = recipe.items,
output = {result.item:to_string()},
}
local replacement = new_input.items[1]
if not replacement:is_empty() then
data.output[2] = replacement:to_string()
end
cache_recipe(data)
end
end
end
end
-- Cache custom recipes
for _, data in pairs(temp_recipes) do
if not data.hidden then
add_to_craftguides(data)
end
cache_recipe(data)
end
temp_recipes = nil
end
-- Slightly hacky way to be the first function called
table.insert(minetest.registered_on_mods_loaded, 1, cache_all_recipes)
minetest.callback_origins[cache_all_recipes] = {
mod = "technic",
name = "register_on_mods_loaded",
}

View file

@ -0,0 +1,72 @@
local S = technic.getter
function technic.register_solar_array(nodename, data)
local _, modname, name, def = technic.register_compat_v1_to_v2(nodename, data, "solar_array")
assert(def.tier, "Technic register_solar_array requires `tier` field")
local tier = def.tier
local ltier = string.lower(tier)
local infotext = S("Arrayed Solar @1 Generator", S(tier))
local run = function(pos, node)
-- The action here is to make the solar array produce power
-- Power is dependent on the light level and the height above ground
-- There are many ways to cheat by using other light sources like lamps.
-- As there is no way to determine if light is sunlight that is just a shame.
-- To take care of some of it solar panels do not work outside daylight hours or if
-- built below 0m
local pos1 = { y = pos.y + 1, x = pos.x, z = pos.z }
minetest.load_area(pos1)
local light = minetest.get_node_light(pos1, nil)
local time_of_day = minetest.get_timeofday()
local meta = minetest.get_meta(pos)
light = light or 0
-- turn on array only during day time and if sufficient light
-- I know this is counter intuitive when cheating by using other light sources.
if light >= 12 and time_of_day >= 0.24 and time_of_day <= 0.76 and pos.y > 0 then
local charge_to_give = math.floor((light + pos.y) * def.power)
charge_to_give = math.max(charge_to_give, 0)
charge_to_give = math.min(charge_to_give, def.power * 50)
meta:set_string("infotext", S("@1 Active (@2)", infotext, technic.EU_string(charge_to_give)))
meta:set_int(tier.."_EU_supply", charge_to_give)
else
meta:set_string("infotext", S("@1 Idle", infotext))
meta:set_int(tier.."_EU_supply", 0)
end
end
def.tiles = def.tiles or {
modname.."_"..name.."_top.png",
modname.."_"..name.."_bottom.png",
modname.."_"..name.."_side.png",
modname.."_"..name.."_side.png",
modname.."_"..name.."_side.png",
modname.."_"..name.."_side.png"
}
def.groups = def.groups or {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, ["technic_"..ltier]=1, axey=2, handy=1}
def.is_ground_content = false
def._mcl_blast_resistance = 1
def._mcl_hardness = 0.8
def.connect_sides = def.connect_sides or {"bottom"}
def.sounds = def.sounds or technic.sounds.node_sound_wood_defaults()
def.description = def.description or S("Arrayed Solar @1 Generator", S(tier))
def.active = def.active or false
def.drawtype = def.drawtype or "nodebox"
def.paramtype = def.paramtype or "light"
def.node_box = def.nodebox or {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
}
def.on_construct = def.on_construct or function(pos)
local meta = minetest.get_meta(pos)
meta:set_int(tier.."_EU_supply", 0)
end
def.technic_run = def.technic_run or run
minetest.register_node(nodename, def)
technic.register_machine(tier, nodename, technic.producer)
end

View file

@ -0,0 +1,219 @@
-- The supply converter is a generic device which can convert from
-- LV to MV and back, and HV to MV and back.
-- The machine is configured by the wiring below and above it.
--
-- It works like this:
-- The top side is setup as the receiver side, the bottom as the producer side.
-- Once the receiver side is powered it will deliver power to the other side.
-- Unused power is wasted just like any other producer!
local digilines_path = minetest.get_modpath("digilines")
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
local function set_supply_converter_formspec(meta)
local formspec = "size[5,2.25]"..
"field[0.3,0.5;2,1;power;"..S("Input Power")..";${power}]"
if digilines_path then
formspec = formspec..
"field[2.3,0.5;3,1;channel;"..S("Digiline Channel")..";${channel}]"
end
-- The names for these toggle buttons are explicit about which
-- state they'll switch to, so that multiple presses (arising
-- from the ambiguity between lag and a missed press) only make
-- the single change that the user expects.
if meta:get_int("mesecon_mode") == 0 then
formspec = formspec.."button[0,1;5,1;mesecon_mode_1;"..S("Ignoring Mesecon Signal").."]"
else
formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
end
if meta:get_int("enabled") == 0 then
formspec = formspec.."button[0,1.75;5,1;enable;"..S("@1 Disabled", S("Supply Converter")).."]"
else
formspec = formspec.."button[0,1.75;5,1;disable;"..S("@1 Enabled", S("Supply Converter")).."]"
end
meta:set_string("formspec", formspec)
end
local supply_converter_receive_fields = function(pos, formname, fields, sender)
if not sender or minetest.is_protected(pos, sender:get_player_name()) then
return
end
local meta = minetest.get_meta(pos)
local power = nil
if fields.power then
power = tonumber(fields.power) or 0
power = math.max(power, 0)
power = math.min(power, 10000)
power = 100 * math.floor(power / 100)
if power == meta:get_int("power") then power = nil end
end
if power then meta:set_int("power", power) end
if fields.channel then meta:set_string("channel", fields.channel) end
if fields.enable then meta:set_int("enabled", 1) end
if fields.disable then meta:set_int("enabled", 0) end
if fields.mesecon_mode_0 then meta:set_int("mesecon_mode", 0) end
if fields.mesecon_mode_1 then meta:set_int("mesecon_mode", 1) end
set_supply_converter_formspec(meta)
end
local mesecons = {
effector = {
action_on = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 1)
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 0)
end
}
}
local digiline_def = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if type(msg) ~= "string" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
msg = msg:lower()
if msg == "get" then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
enabled = meta:get_int("enabled"),
power = meta:get_int("power"),
mesecon_mode = meta:get_int("mesecon_mode")
})
return
elseif msg == "off" then
meta:set_int("enabled", 0)
elseif msg == "on" then
meta:set_int("enabled", 1)
elseif msg == "toggle" then
local onn = meta:get_int("enabled")
onn = 1-onn -- Mirror onn with pivot 0.5, so switch between 1 and 0.
meta:set_int("enabled", onn)
elseif msg:sub(1, 5) == "power" then
local power = tonumber(msg:sub(7))
if not power then
return
end
power = math.max(power, 0)
power = math.min(power, 10000)
power = 100 * math.floor(power / 100)
meta:set_int("power", power)
elseif msg:sub(1, 12) == "mesecon_mode" then
meta:set_int("mesecon_mode", tonumber(msg:sub(14)))
else
return
end
set_supply_converter_formspec(meta)
end
},
}
local run = function(pos, node, run_stage)
-- run only in producer stage.
if run_stage == technic.receiver then
return
end
local remain = 0.9
-- Machine information
local machine_name = S("Supply Converter")
local meta = minetest.get_meta(pos)
local enabled = meta:get_int("enabled") == 1 and
(meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
local demand = enabled and meta:get_int("power") or 0
local pos_up = {x=pos.x, y=pos.y+1, z=pos.z}
local pos_down = {x=pos.x, y=pos.y-1, z=pos.z}
local name_up = minetest.get_node(pos_up).name
local name_down = minetest.get_node(pos_down).name
local from = technic.get_cable_tier(name_up)
local to = technic.get_cable_tier(name_down)
if from and to then
local input = meta:get_int(from.."_EU_input")
if (technic.get_timeout(from, pos) <= 0) or (technic.get_timeout(to, pos) <= 0) then
-- Supply converter timed out, either RE or PR network is not running anymore
input = 0
end
meta:set_int(from.."_EU_demand", demand)
meta:set_int(from.."_EU_supply", 0)
meta:set_int(to.."_EU_demand", 0)
meta:set_int(to.."_EU_supply", input * remain)
meta:set_string("infotext", S("@1 (@2 @3 -> @4 @5)", machine_name,
technic.EU_string(input), from,
technic.EU_string(input * remain), to))
else
meta:set_string("infotext", S("@1 Has Bad Cabling", machine_name))
if to then
meta:set_int(to.."_EU_supply", 0)
end
if from then
meta:set_int(from.."_EU_demand", 0)
end
return
end
end
minetest.register_node("technic:supply_converter", {
description = S("Supply Converter"),
tiles = {
"technic_supply_converter_tb.png"..cable_entry,
"technic_supply_converter_tb.png"..cable_entry,
"technic_supply_converter_side.png",
"technic_supply_converter_side.png",
"technic_supply_converter_side.png",
"technic_supply_converter_side.png"
},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_all_tiers=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"top", "bottom"},
sounds = technic.sounds.node_sound_wood_defaults(),
on_receive_fields = supply_converter_receive_fields,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Supply Converter"))
meta:set_int("power", 10000)
meta:set_int("enabled", 1)
meta:set_int("mesecon_mode", 0)
meta:set_int("mesecon_effect", 0)
set_supply_converter_formspec(meta)
end,
mesecons = mesecons,
digiline = digiline_def,
technic_run = run,
technic_on_disable = run,
})
minetest.register_craft({
output = 'technic:supply_converter 1',
recipe = {
{'basic_materials:gold_wire', 'technic:rubber', 'technic:doped_silicon_wafer'},
{'technic:mv_transformer', 'technic:machine_casing', 'technic:lv_transformer'},
{'technic:mv_cable', 'technic:rubber', 'technic:lv_cable'},
},
replacements = { {"basic_materials:gold_wire", "basic_materials:empty_spool"}, },
})
for tier, machines in pairs(technic.machines) do
technic.register_machine(tier, "technic:supply_converter", technic.producer_receiver)
end

View file

@ -0,0 +1,198 @@
-- See also technic/doc/api.md
local mesecons_path = minetest.get_modpath("mesecons")
local digilines_path = minetest.get_modpath("digilines")
local S = technic.getter
local mat = technic.materials
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_craft({
output = "technic:switching_station",
recipe = {
{"", "technic:lv_transformer", ""},
{mat.copper_ingot, "technic:machine_casing", mat.copper_ingot},
{"technic:lv_cable", "technic:lv_cable", "technic:lv_cable"}
}
})
local function start_network(pos)
local tier = technic.sw_pos2tier(pos)
if not tier then
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("@1 Has No Network", S("Switching Station")))
else
local network_id = technic.sw_pos2network(pos) or technic.create_network(pos)
local network = network_id and technic.networks[network_id]
if network and technic.switch_insert(pos, network) > 0 then
technic.activate_network(network_id)
end
end
end
local mesecon_def
if mesecons_path then
mesecon_def = {effector = {
rules = mesecon.rules.default,
}}
end
minetest.register_node("technic:switching_station",{
description = S("Switching Station"),
tiles = {
"technic_water_mill_top_active.png",
"technic_water_mill_top_active.png"..cable_entry,
"technic_water_mill_top_active.png",
"technic_water_mill_top_active.png",
"technic_water_mill_top_active.png",
"technic_water_mill_top_active.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, technic_all_tiers=1, axey=2, handy=1},
is_ground_content = false,
_mcl_blast_resistance = 1,
_mcl_hardness = 0.8,
connect_sides = {"bottom"},
sounds = technic.sounds.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Switching Station"))
meta:set_string("formspec", "field[channel;"..S("Digiline Channel")..";${channel}]")
start_network(pos)
-- start nodetimer
minetest.get_node_timer(pos):start(1.0)
end,
on_destruct = function(pos)
-- Remove network when last switching stations is removed
local network_id = technic.sw_pos2network(pos)
local network = network_id and technic.networks[network_id]
if network and technic.switch_remove(pos, network) < 1 then
technic.remove_network(network_id)
end
end,
on_receive_fields = function(pos, formname, fields, sender)
if not fields.channel then
return
end
local plname = sender:get_player_name()
if minetest.is_protected(pos, plname) then
minetest.record_protection_violation(pos, plname)
return
end
local meta = minetest.get_meta(pos)
meta:set_string("channel", fields.channel)
end,
-- nodetimer for network activation
on_timer = function(pos)
local network_id = technic.sw_pos2network(pos)
-- Check if network is overloaded / conflicts with another network
if network_id then
local infotext
local meta = minetest.get_meta(pos)
if technic.is_overloaded(network_id) then
local remaining = technic.reset_overloaded(network_id)
if remaining > 0 then
infotext = S("@1 Network Overloaded, Restart in @2ms", S("Switching Station"), remaining / 1000)
else
infotext = S("@1 Restarting Network", S("Switching Station"))
end
technic.network_infotext(network_id, infotext)
else
-- Network exists and is not overloaded, reactivate network
technic.activate_network(network_id)
infotext = technic.network_infotext(network_id)
-- If mesecon signal enabled and power supply or demand changed then send them via digilines.
if mesecons_path and digilines_path and mesecon.is_powered(pos) then
local network = technic.networks[network_id]
if meta:get_int("supply") ~= network.supply or meta:get_int("demand") ~= network.demand then
meta:set_int("supply", network.supply)
meta:set_int("demand", network.demand)
local channel = meta:get_string("channel")
digilines.receptor_send(pos, technic.digilines.rules, channel, {
supply = network.supply,
demand = network.demand
})
end
end
end
meta:set_string("infotext", infotext)
else
-- Network does not exist yet, attempt to create new network here
start_network(pos)
end
-- restart the nodetimer again
return true
end,
-- mesecons
mesecons = mesecon_def,
-- digiline
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if msg ~= "GET" and msg ~= "get" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local network_id = technic.sw_pos2network(pos)
local network = network_id and technic.networks[network_id]
if network then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
supply = network.supply,
demand = network.demand,
lag = network.lag
})
else
digilines.receptor_send(pos, technic.digilines.rules, channel, {
error = "No network",
})
end
end
},
},
})
-----------------------------------------------
-- The action code for the switching station --
-----------------------------------------------
-- Timeout ABM
-- Timeout for a node in case it was disconnected from the network
-- A node must be touched by the station continuously in order to function
minetest.register_abm({
label = "Machines: timeout check",
nodenames = {"group:technic_machine"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
if not technic.machine_tiers[node.name] then
-- https://github.com/mt-mods/technic/issues/123
return
end
-- Check for machine timeouts for all tiers
local tiers = technic.machine_tiers[node.name]
local timed_out = true
for _, tier in ipairs(tiers) do
local timeout = technic.get_timeout(tier, pos)
if timeout > 0 then
technic.touch_node(tier, pos, timeout - 1)
timed_out = false
end
end
-- If all tiers for machine timed out take action
if timed_out then
technic.disable_machine(pos, node)
end
end,
})

View file

@ -0,0 +1,76 @@
local S = technic.getter
-- the interval between technic_run calls
local technic_run_interval = 1.0
local set_default_timeout = technic.set_default_timeout
-- iterate over all collected switching stations and execute the technic_run function
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < technic_run_interval or not technic.powerctrl_state then
return
end
timer = 0
local max_lag = technic.get_max_lag()
-- slow down technic execution if the lag is higher than usual
if max_lag > 5.0 then
technic_run_interval = 5.0
elseif max_lag > 2.0 then
technic_run_interval = 4.0
elseif max_lag > 1.5 then
technic_run_interval = 3.0
elseif max_lag > 1.0 then
technic_run_interval = 1.5
else
-- normal run_interval
technic_run_interval = 1.0
end
set_default_timeout(math.ceil(technic_run_interval) + 1)
local now = minetest.get_us_time()
for network_id, network in pairs(technic.active_networks) do
if network.timeout > now and not technic.is_overloaded(network_id) then
-- station active
if network.skip > 0 then
network.skip = network.skip - 1
else
local start = minetest.get_us_time()
technic.network_run(network_id)
local switch_diff = network.average_lag(minetest.get_us_time() - start)
-- set lag in microseconds into the "lag" meta field
network.lag = switch_diff
-- overload detection
if switch_diff > 250000 then
network.skip = 30
elseif switch_diff > 150000 then
network.skip = 20
elseif switch_diff > 75000 then
network.skip = 10
elseif switch_diff > 50000 then
network.skip = 2
end
if network.skip > 0 then
-- calculate efficiency in percent and display it
local efficiency = math.floor(1/network.skip*100)
technic.network_infotext(network_id,
S("Polyfuse triggered, current efficiency: @1%, generated lag: @2 ms",
efficiency, math.floor(switch_diff/1000)))
-- remove laggy network from active index
-- it will be reactivated when a player is near it
technic.active_networks[network_id] = nil
end
end
else
-- network timed out
technic.active_networks[network_id] = nil
end
end
end)