techage/collider/detector.lua
2024-01-21 11:55:48 +01:00

449 lines
13 KiB
Lua

--[[
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'},
},
})