--[[ TechAge ======= Copyright (C) 2019-2023 Joachim Stolberg AGPL v3 See LICENSE.txt for more information TA4 Detector as part of the Collider ]]-- -- for lazy programmers local M = minetest.get_meta local S = techage.S local S2P = minetest.string_to_pos local P2S = minetest.pos_to_string local getpos = techage.assemble.get_pos local CYCLE_TIME = 2 local TNO_MAGNETS = 22 local IMPROBABILITY = 40 -- every 40 min -- one point per 40 min: check every 20 s => factor = 40 * 3 = 120 IMPROBABILITY = (minetest.settings:get("techage_expoint_rate_in_min") or 40) * 3 local TIME_SLOTS = 10 local Schedule = {[0] = -- Route: 0 = forward, 1 = right, 2 = backward, 3 = left -- Gas left/right {name = "techage:ta4_collider_pipe_inlet", yoffs = 1, route = {3,3,3,2}, check = techage.gas_inlet_check}, {name = "techage:ta4_collider_pipe_inlet", yoffs = 1, route = {1,1,1,2}, check = techage.gas_inlet_check}, -- Power left/right {name = "techage:ta4_collider_cable_inlet", yoffs = 2, route = {3,3,3}, check = techage.power_inlet_check}, {name = "techage:ta4_collider_cable_inlet", yoffs = 2, route = {1,1,1}, check = techage.power_inlet_check}, -- Cooler {name = "techage:ta4_collider_pipe_inlet", yoffs = 0, route = {0}, check = techage.cooler_check}, {name = "techage:ta4_collider_pipe_inlet", yoffs = 2, route = {0}, check = techage.cooler_check}, -- Air outlet {name = "techage:ta4_collider_pipe_outlet", yoffs = 2, route = {}, check = techage.air_outlet_check}, -- All nodes {name = "shell", yoffs = 0, route = {}, check = nil}, } local function play_sound(pos) minetest.sound_play("techage_hum", { pos = pos, gain = 0.5, max_hear_distance = 10, }) end local function terminal_message(pos, msg) local term_num = M(pos):contains("term_num") and M(pos):get_string("term_num") local own_num = M(pos):get_string("node_number") if term_num and own_num then techage.send_single(own_num, term_num, "text", msg) end end local function experience_points(pos) if math.random(IMPROBABILITY) == 1 then local owner = M(pos):get_string("owner") local own_num = M(pos):get_string("node_number") local player = minetest.get_player_by_name(owner) if player then if techage.add_expoint(player, own_num) then terminal_message(pos, "Experience point reached!") end end end end local function check_shell(pos, param2) local pos1 = getpos(pos, param2, {3,3,3,2}, 0) local pos2 = getpos(pos, param2, {1,1,1,0}, 2) local _, tbl = minetest.find_nodes_in_area(pos1, pos2, {"techage:ta4_detector_magnet", "techage:ta4_colliderblock", "default:obsidian_glass"}) if tbl["techage:ta4_detector_magnet"] < 16 then return false, "Magnet missing" elseif tbl["techage:ta4_colliderblock"] < 31 then return false, "Steel block missing" elseif tbl["default:obsidian_glass"] < 1 then return false, "Obsidian glass missing" end return true end local function check_state(pos) -- Cyclically check all connections local param2 = minetest.get_node(pos).param2 local nvm = techage.get_nvm(pos) nvm.ticks = (nvm.ticks or 0) + 1 local idx = nvm.ticks % TIME_SLOTS local item = Schedule[idx] if idx == 1 then nvm.result = true end if item then if item.name == "shell" then local res, err = check_shell(pos, param2) if not res then nvm.result = false nvm.runnning = false terminal_message(pos, (err or "unknown") .. "!!!") return nvm.result end else local pos2 = getpos(pos, param2, item.route, item.yoffs) local nvm2 = techage.get_nvm(pos2) local meta2 = M(pos2) local node2 = minetest.get_node(pos2) if item.name == node2.name then local res, err = item.check(pos2, node2, meta2, nvm2) --print("check_state", idx, res, err) if not res then nvm.result = false nvm.runnning = false terminal_message(pos, (err or "unknown") .. "!!!") return nvm.result end else nvm.result = false nvm.runnning = false terminal_message(pos, "Detector defect!!!") end end elseif idx == #Schedule + 1 then return nvm.result end end local function add_laser(pos) local param2 = minetest.get_node(pos).param2 local pos1 = getpos(pos, param2, {3,3}, 1) local pos2 = getpos(pos, param2, {1,1,1}, 1) techage.del_laser(pos) techage.add_laser(pos, pos1, pos2) end local function create_task(pos, task) local mem = techage.get_mem(pos) if not mem.co then mem.co = coroutine.create(task) end local _, err = coroutine.resume(mem.co, pos) if err then mem.co = nil --print(err) return end minetest.after(0.4, create_task, pos, task) end -- Call on_cyclic_check of all magents so that the magnets don't need a FLB. local function magnets_on_cyclic_check(pos, nvm) local ndef = minetest.registered_nodes["techage:ta4_magnet"] for idx,pos2 in ipairs(nvm.magnet_positions or {}) do local res = ndef.on_cyclic_check(pos2) if res == -2 then terminal_message(pos, "Magnet #" .. idx .. " defect!!!") return false elseif res == -1 then terminal_message(pos, "Vacuum defect!!!") techage.air_outlet_reset({x=pos.x, y=pos.y + 2, z=pos.z}) return false end end return true end -- Turn off all magnets so that they don't consume power local function magnets_turn_off(pos, nvm) local ndef = minetest.registered_nodes["techage:ta4_magnet"] for idx,pos2 in ipairs(nvm.magnet_positions or {}) do ndef.on_turn_off(pos2) end end local function cable_inlets_turn_on_off(pos, on) local turn_on_off = function(pos, param2, item) local pos2 = getpos(pos, param2, item.route, item.yoffs) local node2 = minetest.get_node(pos2) if item.name == node2.name then local nvm = techage.get_nvm(pos2) techage.power_inlet_turn_on_off(pos2, nvm, on) end end local param2 = minetest.get_node(pos).param2 turn_on_off(pos, param2, Schedule[2]) turn_on_off(pos, param2, Schedule[3]) end minetest.register_node("techage:ta4_detector_core", { description = S("TA4 Collider Detector Core"), tiles = { -- up, down, right, left, back, front "default_steel_block.png", "default_steel_block.png", "default_steel_block.png^techage_collider_detector_core.png", "default_steel_block.png^techage_collider_detector_core.png", "default_steel_block.png^techage_collider_detector_core.png", "default_steel_block.png^techage_collider_detector_core.png", }, paramtype2 = "facedir", groups = {cracky = 1}, is_ground_content = false, sounds = default.node_sound_metal_defaults(), after_place_node = function(pos, placer, itemstack) local nvm = techage.get_nvm(pos) local meta = M(pos) local own_num = techage.add_node(pos, "techage:ta4_detector_core") meta:set_string("node_number", own_num) meta:set_string("owner", placer:get_player_name()) M({x=pos.x, y=pos.y - 1, z=pos.z}):set_string("infotext", S("TA4 Collider Detector") .. " " .. own_num) minetest.get_node_timer(pos):start(CYCLE_TIME) end, on_timer = function(pos, elapsed) local nvm = techage.get_nvm(pos) if nvm.running then if not magnets_on_cyclic_check(pos, nvm) then techage.del_laser(pos) terminal_message(pos, "Detector stopped.") magnets_turn_off(pos, nvm) cable_inlets_turn_on_off(pos, false) nvm.running = false nvm.magnet_positions = nil else local res = check_state(pos) if res == true then experience_points(pos) add_laser(pos) if nvm.ticks <= TIME_SLOTS then -- only once terminal_message(pos, "Detector running.") end elseif res == false then techage.del_laser(pos) magnets_turn_off(pos, nvm) cable_inlets_turn_on_off(pos, false) nvm.running = false nvm.magnet_positions = nil terminal_message(pos, "Detector stopped.") end if nvm.running then play_sound(pos) end end end return true end, after_dig_node = function(pos, oldnode, oldmetadata, digger) techage.on_remove_collider(digger) techage.remove_node(pos, oldnode, oldmetadata) techage.del_mem(pos) end, }) local function check_expr(own_num, term_num, text, expr) techage.send_single(own_num, term_num, "text", text .. "..." .. (expr and "ok" or "error!!!")) return expr end local function start_task(pos) local term_num = M(pos):contains("term_num") and M(pos):get_string("term_num") local param2 = minetest.get_node(pos).param2 local pos2 = getpos(pos, param2, {3,3,3}, 1) local own_num = M(pos):get_string("node_number") local nvm = techage.get_nvm(pos) nvm.magnet_positions = {} if term_num and param2 and pos2 then techage.send_single(own_num, term_num, "text", "#### Start ####") coroutine.yield() local resp = techage.tube_inlet_command(pos2, "enumerate", 1) if not check_expr(own_num, term_num, "- Check number of magnets", resp == TNO_MAGNETS) then nvm.locked = false return end coroutine.yield() techage.send_single(own_num, term_num, "text", "- Check position of magnets...") resp = techage.tube_inlet_command(pos2, "distance") if resp ~= true then techage.send_single(own_num, term_num, "append", "#" .. resp .. " defect!!!") nvm.locked = false return end techage.send_single(own_num, term_num, "append", "ok") coroutine.yield() techage.send_single(own_num, term_num, "text", "- Start magnets...") local t = {} for num = 1, TNO_MAGNETS do local resp = techage.tube_inlet_command(pos2, "pos", num) if not resp or type(resp) ~= "table" then techage.send_single(own_num, term_num, "append", "#" .. num .. " defect!!!") nvm.magnet_positions = nil nvm.locked = false return else t[#t + 1] = resp end coroutine.yield() end nvm.magnet_positions = t techage.send_single(own_num, term_num, "append", "ok") cable_inlets_turn_on_off(pos, true) coroutine.yield() techage.send_single(own_num, term_num, "text", "- Check magnets...") -- The check will be performed by the timer, so wait 5 sec. for i = 1,14 do coroutine.yield() end if nvm.magnet_positions then techage.send_single(own_num, term_num, "append", "ok") else nvm.locked = false return end coroutine.yield() techage.send_single(own_num, term_num, "text", "- Check detector...") for _,item in ipairs(Schedule)do if item.name == "shell" then local res, err = check_shell(pos, param2) if not res then techage.send_single(own_num, term_num, "append", err .. "!!!") nvm.magnet_positions = nil nvm.locked = false cable_inlets_turn_on_off(pos, false) return end else local pos2 = getpos(pos, param2, item.route, item.yoffs) local nvm2 = techage.get_nvm(pos2) local meta2 = M(pos2) local node2 = minetest.get_node(pos2) if item.name == node2.name then local res, err = item.check(pos2, node2, meta2, nvm2) if not res then techage.send_single(own_num, term_num, "append", err .. "!!!") nvm.magnet_positions = nil nvm.locked = false cable_inlets_turn_on_off(pos, false) return end else techage.send_single(own_num, term_num, "append", "defect!!!") nvm.magnet_positions = nil nvm.locked = false cable_inlets_turn_on_off(pos, false) return end coroutine.yield() end end techage.send_single(own_num, term_num, "append", "ok") coroutine.yield() techage.send_single(own_num, term_num, "text", "Collider starting...") nvm.ticks = 0 nvm.running = true end end local function test_magnet(pos, payload) local term_num = M(pos):contains("term_num") and M(pos):get_string("term_num") local param2 = minetest.get_node(pos).param2 local pos2 = getpos(pos, param2, {3,3,3}, 1) local own_num = M(pos):get_string("node_number") local magnet_num = tonumber(payload) local res, err = techage.tube_inlet_command(pos2, "test", magnet_num) if res then techage.send_single(own_num, term_num, "text", "magnet #" .. magnet_num .. ": ok") else techage.send_single(own_num, term_num, "text", "magnet #" .. magnet_num .. ": " .. (err or "unknown error") .. "!!!") end end techage.register_node({"techage:ta4_detector_core"}, { on_recv_message = function(pos, src, topic, payload) local nvm = techage.get_nvm(pos) if topic == "connect" then M(pos):set_string("term_num", src) return true elseif topic == "start" then -- Worker block nvm.locked = true create_task(pos, start_task) return true elseif topic == "stop" then nvm.running = false techage.del_laser(pos) nvm.locked = false magnets_turn_off(pos, nvm) cable_inlets_turn_on_off(pos, false) nvm.magnet_positions = nil return "Detector stopped." elseif topic == "status" then if nvm.running == true then return "running" elseif nvm.result == false then return "fault" else return "stopped" end elseif topic == "test"then if payload and tonumber(payload) then test_magnet(pos, payload) return true else return "Invalid magnet number" end elseif topic == "points" then local owner = M(pos):get_string("owner") local player = minetest.get_player_by_name(owner) if player then local points = techage.get_expoints(player) return "Ex. Points = " .. points end else return "unsupported" end end, on_node_load = function(pos) minetest.get_node_timer(pos):start(CYCLE_TIME) end, }) minetest.register_craft({ output = "techage:ta4_detector_core", recipe = { {'techage:aluminum', 'basic_materials:heating_element', 'default:steel_ingot'}, {'default:diamond', 'techage:ta4_wlanchip', 'techage:electric_cableS'}, {'default:steel_ingot', '', 'techage:aluminum'}, }, })