cctweaked_drone/lib/materiel.lua
2026-07-17 18:07:54 +02:00

286 lines
9.4 KiB
Lua

--------------------------------------------------------------------
-- lib/materiel.lua : resolution des peripheriques et adaptateurs.
-- Tous les formats sont VERIFIES en jeu (inventaire du 17/07/2026).
--
-- Directs : altitude_sensor (top), 2 Create_DisplayLink (faces dans
-- conf.ECRANS), ender modem (gps), moteur diesel en bottom pilote
-- par redstone analogique (15=arret, 14=mini, 0=maxi).
-- Reseau : RSC (roles calibres), velocity_sensor x3 (axes/directions
-- calibres), gimbal_sensor (axes calibres), optical_sensor,
-- Create_Stressometer, redstone_relay (joystick calibre),
-- linked_typewriter, monitor, 2 cuves create:fluid_tank.
--------------------------------------------------------------------
local Materiel = {}
local function trouver(type_, obligatoire, journal, note)
local p = peripheral.find(type_)
if not p and obligatoire then
error("peripherique manquant: " .. type_, 0)
end
if not p and journal then
journal.alerte(type_ .. " introuvable" .. (note and (": " .. note) or ""))
end
return p
end
function Materiel.initialiser(conf, etat, journal)
local m = {}
m.rpm = {} -- role -> { v, tChange } (pour l'affichage)
local posCache = nil, nil
local posDate = 0
m.altitude = trouver("altitude_sensor", true)
m.gimbal = trouver("gimbal_sensor", true)
m.optical = trouver("optical_sensor", true)
m.moniteur = trouver("monitor", true)
m.relay = trouver("redstone_relay", false, journal, "joystick inactif")
m.stresso = trouver("Create_Stressometer", false, journal,
"detection moteur et surcharge inactives")
m.typewriter = trouver("linked_typewriter", false, journal,
"consigne au moniteur uniquement")
-- les deux display links, identifies par leur face (conf.ECRANS)
local function ecran(face, usage)
local p = peripheral.wrap(face)
if not p or peripheral.getType(face) ~= "Create_DisplayLink" then
error(("Create_DisplayLink attendu en '%s' (%s), voir conf.ECRANS")
:format(face, usage), 0)
end
return p
end
m.ecranConduite = ecran(conf.ECRANS.conduite, "conduite")
m.ecranCarburant = ecran(conf.ECRANS.carburant, "carburant")
-- rotation speed controllers et velocity sensors: les affectations
-- (etat.roles / etat.velocite) sont rechargeables A CHAUD apres
-- une recalibration via m.rafraichirAffectations()
m.rsc, m.velocite = {}, {}
for _, nom in ipairs(peripheral.getNames()) do
local type_ = peripheral.getType(nom)
if type_ == "Create_RotationSpeedController" then
m.rsc[nom] = peripheral.wrap(nom)
elseif type_ == "velocity_sensor" then
m.velocite[nom] = peripheral.wrap(nom)
end
end
function m.rafraichirAffectations()
m.rscParRole, m.veloParAxe = {}, {}
for nom, p in pairs(m.rsc) do
local role = etat.roles[nom]
if role then m.rscParRole[role] = p end
end
for nom, p in pairs(m.velocite) do
local aff = etat.velocite[nom]
if aff then m.veloParAxe[aff.axe] = { p = p, signe = aff.signe } end
end
end
m.rafraichirAffectations()
-- cuves de carburant (create:fluid_tank; exclut le moteur diesel,
-- qui est fluid_storage mais pas fluid_tank)
m.cuves = {}
for _, nom in ipairs(peripheral.getNames()) do
if peripheral.hasType(nom, "create:fluid_tank") then
table.insert(m.cuves, peripheral.wrap(nom))
end
end
if #m.cuves == 0 then
journal.alerte("aucune cuve create:fluid_tank: suivi carburant inactif")
end
------------------------------------------------------------------
-- LECTURES
------------------------------------------------------------------
function m.lireAltitude()
return m.altitude.getHeight()
end
-- angles bruts du gimbal (table de 2)
function m.lireAnglesBruts()
local a = m.gimbal.getAngles()
return a[1] or 0, a[2] or 0
end
-- assiette NORMALISEE via la calibration gimbal :
-- tangage negatif = nez bas, roulis negatif = penche a gauche
function m.lireAssiette()
local g = etat.gimbal
if not g then return m.lireAnglesBruts() end
local a = m.gimbal.getAngles()
return (a[g.indexTangage] or 0) * g.signeTangage,
(a[g.indexRoulis] or 0) * g.signeRoulis
end
function m.lireDistanceSol()
return m.optical.getDistance()
end
function m.auSol()
return m.lireDistanceSol() <= conf.DIST_SOL
end
-- sol detecte ET posable (pas d'eau/lave: garde-fou ocean)
function m.solPosable()
if not m.optical.hasHit() then return false end
local bloc = m.optical.getBlock()
return not conf.BLOCS_INTERDITS[bloc]
end
function m.horizontal()
local t, r = m.lireAssiette()
return math.abs(t) <= conf.ANGLE_MAX and math.abs(r) <= conf.ANGLE_MAX
end
-- vitesse le long d'un axe calibre ("avant"|"lateral"|"vertical")
-- signe: positif = avant / gauche / haut
function m.lireVitesseAxe(axe)
local c = m.veloParAxe[axe]
if not c then return 0 end
return (c.p.getVelocity() or 0) * c.signe
end
function m.vitesseHorizontale()
local va = m.lireVitesseAxe("avant")
local vl = m.lireVitesseAxe("lateral")
return math.sqrt(va * va + vl * vl)
end
-- joystick calibre: avance (devant-derriere), virage (gauche-droite)
-- dans -1..1; pas de zone morte (joystick de mod, zero franc)
function m.lireJoystick()
local j = etat.joystick
if not (m.relay and j) then return 0, 0 end
local function lire(direction)
local face = j[direction]
if not face then return 0 end
return m.relay.getAnalogInput(face) / 15
end
return lire("devant") - lire("derriere"),
lire("gauche") - lire("droite")
end
function m.lireFacesRelay()
if not m.relay then return {} end
local lectures = {}
for _, face in ipairs({ "top", "bottom", "left", "right",
"front", "back" }) do
lectures[face] = m.relay.getAnalogInput(face)
end
return lectures
end
-- typewriter: table des codes de touches actuellement pressees
function m.lireTouches()
if not m.typewriter then return {} end
return m.typewriter.getPressedKeyCodes() or {}
end
-- stressometer (formats verifies): ratio 0..1, ou nil
function m.lireStress()
if not m.stresso then return nil end
local cap = m.stresso.getStressCapacity()
if not cap or cap <= 0 then return nil end
return m.stresso.getStress() / cap
end
-- valeurs brutes (su) pour l'affichage: stress, capacite
function m.lireStressBrut()
if not m.stresso then return nil end
return m.stresso.getStress(), m.stresso.getStressCapacity()
end
-- capacite > 0 <=> le moteur tourne (verifie: capacite 0 a l'arret)
function m.moteurTourne()
if not m.stresso then return true end -- indetectable: on suppose oui
return (m.stresso.getStressCapacity() or 0) > 0
end
-- carburant: quantite totale (mB) et capacite totale
function m.lireCarburant()
if #m.cuves == 0 then return nil end
local quantite, capacite = 0, 0
for _, cuve in ipairs(m.cuves) do
for _, t in ipairs(cuve.tanks() or {}) do
quantite = quantite + (t.amount or 0)
capacite = capacite + (t.capacity or conf.CARBURANT.CAPACITE_TANK)
end
end
if capacite <= 0 then capacite = conf.CARBURANT.CAPACITE_TANK * #m.cuves end
return quantite, capacite
end
-- ACTUALISATION GPS (BLOQUANTE, jusqu'a 2 s): a appeler depuis une
-- tache dediee uniquement
function m.actualiserPosition()
local x, y, z = gps.locate(2)
if x then
posCache = { x = x + conf.OFFSET_X, y = y + conf.OFFSET_Y,
z = z + conf.OFFSET_Z }
posDate = os.clock()
end
end
-- position GPS en CACHE (non bloquante); nil si absente ou > 8 s
function m.lirePosition()
if not posCache or os.clock() - posDate > 8 then return nil end
return posCache.x, posCache.y, posCache.z
end
------------------------------------------------------------------
-- SORTIES
------------------------------------------------------------------
function m.reglerMoteur(niveau) -- 0 (arret) .. 15 (maxi)
niveau = math.max(0, math.min(15, math.floor(niveau + 0.5)))
redstone.setAnalogOutput("bottom", 15 - niveau)
end
function m.reglerRsc(role, vitesse)
local p = m.rscParRole[role]
if not p then return end
vitesse = math.floor(vitesse + 0.5)
local suivi = m.rpm[role]
if not suivi or suivi.v ~= vitesse then
m.rpm[role] = { v = vitesse, tChange = os.clock() }
end
p.setTargetSpeed(vitesse)
end
function m.toutArreter()
for _, p in pairs(m.rsc) do p.setTargetSpeed(0) end
for role, suivi in pairs(m.rpm) do
if suivi.v ~= 0 then m.rpm[role] = { v = 0, tChange = os.clock() } end
end
end
------------------------------------------------------------------
-- DIAGNOSTIC DES CALIBRATIONS
------------------------------------------------------------------
function m.helicesManquantes()
local manquantes = {}
for _, role in ipairs({ "lb", "rb", "lf", "rf" }) do
if not m.rscParRole[role] then table.insert(manquantes, role) end
end
return manquantes
end
function m.propulseursManquants()
local manquants = {}
for _, role in ipairs({ "prop_l", "prop_r" }) do
if not m.rscParRole[role] then table.insert(manquants, role) end
end
return manquants
end
function m.axesManquants()
local manquants = {}
for _, axe in ipairs({ "avant", "lateral", "vertical" }) do
if not m.veloParAxe[axe] then table.insert(manquants, axe) end
end
return manquants
end
return m
end
return Materiel