Skip to main content

Dynamische Geräteauswahl

Einleitung

Anstelle der Schrauben, lassen sich Geräte auch hier via Namen ansprechen. Damit man im Menü nicht ständig alles einzeln schreiben muss, was sich nicht so leicht anpassen lässt, habe ich mir etwas überlegt:

Voraussetzungen

Zunächst einmal benötigt ihr global eine Liste der Geräte. Bei mir sieht die üblicherweise so aus:

local Settings = {
    Devices = {
        FURN            = { prefab = ic.hash("StructureAdvancedFurnace"),        name = ic.hash("Furn"),          desc = "Furnace: ",                allowed_types = { ic.hash("StructureAdvancedFurnace") } },
        VP_IN_HOT       = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.HOT"),       desc = "Heißgaspumpe",             allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        VP_IN_WASTE     = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.WASTE"),     desc = "Wastegaspumpe",            allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        VP_IN_COLD      = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.COLD"),      desc = "Kaltgaspumpe",             allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        PA_TANK_HOT     = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.TANK.HOT"),   desc = "Tanksensor Heißgas",       allowed_types = { ic.hash("StructurePipeAnalysizer")} },
        PA_TANK_WASTE   = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.TANK.WASTE"), desc = "Tanksensor Wastegas",      allowed_types = { ic.hash("StructurePipeAnalysizer")} },
        PA_TANK_COLD    = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.TANK.COLD"),  desc = "Tanksensor Kaltgas",       allowed_types = { ic.hash("StructurePipeAnalysizer")} },
        PA_WASTE        = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.WASTE"),      desc = "Sensor Wasteausgang",      allowed_types = { ic.hash("StructurePipeAnalysizer")} },
        VP_WASTE_OUT    = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.WASTE.OUT"), desc = "Pumpe Wasteablass",        allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        VP_Oxidator     = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.O2"),        desc = "Pumpe Oxidator (O2)",      allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        VP_Reduktor     = { prefab = ic.hash("StructureTurboVolumePump"),        name = ic.hash("TVP.METHANE"),   desc = "Pumpe Reduktor (Methane)", allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump") } },
        PA_Oxidator     = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.O2"),         desc = "Sensor Oxidator (O2)",     allowed_types = { ic.hash("StructurePipeAnalysizer") } },
        PA_Reduktor     = { prefab = ic.hash("StructurePipeAnalysizer"),         name = ic.hash("PA.METHANE"),    desc = "Sensor Reduktor (Methane)",allowed_types = { ic.hash("StructurePipeAnalysizer") } },
        IGN             = { prefab = ic.hash("StructurePipeIgniter"),            name = ic.hash("IGN"),           desc = "Zünder für Gas",           allowed_types = { ic.hash("StructurePipeIgniter")} },
        VP_COLD_FILL    = { prefab = ic.hash("StructurePoweredVentLarge"),       name = ic.hash("PAV.COLD"),      desc = "Kaltgas befüllung",        allowed_types = { ic.hash("StructureTurboVolumePump"), ic.hash("StructureVolumePump"), ic.hash("StructurePoweredVentLarge"), ic.hash("StructurePoweredVent"), ic.hash("StructureActiveVent")} }
    }
}

Diese liste enthält einmal den Hash des Gerätes (prefab), den Namenshash des Gerätes(name), eine Beschreibung für das Menü (desc) und eine Liste an Gerätetypen, die für diesen Einsatz zugelassen sind (allowed_types). 

Hilfsfunktionen

Dann sind noch 3 globale Hilfsfunktionen notwendig:


local function in_array(array, value)
    for _, v in ipairs(array) do
        if v == value then
            return true
        end
    end
    return false
end

function getDeviceByRefId(ref_id) 
    local devices = ic.device_list()
    for _, dev in ipairs(devices) do
        if dev.ref_id == tonumber(ref_id) then
            return dev
        end
    end
    return nil
end

function getDeviceList(sel_prefab, sel_name, allowed) 
    local allowed_device_types = allowed or {}
    local devices = ic.device_list()
    local option_list = {}
    local selected = 0
    local help_i = 1

    for i, dev in ipairs(devices) do
        if #allowed_device_types > 0 then
            if in_array(allowed_device_types, dev.prefab_hash) then
                option_list[help_i] = dev.display_name .. " (ID: ".. dev.ref_id .. ")"
            
                if dev.name_hash == sel_name and dev.prefab_hash == sel_prefab then
                    selected = help_i-1
                end
                help_i = help_i + 1
            end
        else
            option_list[i] = dev.display_name .. " (ID: ".. dev.ref_id .. ")"
            if dev.name_hash == sel_name and dev.prefab_hash == sel_prefab then
                selected = i-1
            end
        end
    end

    return option_list, selected

end
in_array(array, value) ist eine Funktion, die prüft, ob ein Wert in einer Liste vorhanden ist. Sie wird in einer anderen Hilfsfunktion verwendet.
getDeviceByRefId(ref_id) sucht ein Gerät anhand seiner referenceID. Diese ID legt Stationeers für jedes Objekt in der Welt fest und ist im ganzen Spielstand eindeutig. Daher für uns sehr sinnvoll.
getDeviceList(sel_prefab, sel_name, allowed) gibt eine für ein Auswahlmenü (select) vorgefertigte Liste an Geräten zurück. Durch Übergeben des Prefabhashes (sel_prefab) und der Namehashes (sel_name) kann man feststellen, ob und welche Vorauswahl getroffen werden muss. Zusätzlich kann noch die Liste an erlaubten Gerätetypen übergeben werden (allowed), welche die ausgegebene Liste dann auf diese Typen beschränkt.

Anzeige

Ich schreibe alle Anzeigen in eine Funktion, welche immer mit build* beginnt. Zusätzlich habe ich ein Array mit den vorhandenen Screens, daher verwende ich hier kein ui:element() sondern screen_surface:element(). Das könnt ihr aber anders handhaben!

function buildDevices()
    
    local devs = Settings.Devices
    local screen_surface = screens.devices.surface
    local pos_x = 20
    local pos_y = 30
    
    print("Call: buildDevices()")
    screen_surface:clear()

    -- Background
    screen_surface:element({ id = "bg", type = "panel",
        rect = { unit = "px", x = 0, y = 0, w = W, h = H },
        style = { bg = "#0B1120" }
    })
    
    -- Titel
    screen_surface:element({ id = "head_title_text", type = "panel",
        rect = { unit = "px", x = 0, y = 0, w = W, h = 20 },
        style = { bg = "#44DD44" }
    })
    screen_surface:element({ id = "head_title_panel", type = "label",
        rect = { unit = "px", x = 20, y = 0, w = W - 40, h = 20 },
        props = { text = "Geräteliste" },
        style = { font_size = 14, color = "#FFFFFF" }
    })

    for key, dev in pairs(devs) do
        local ol, sel = getDeviceList(dev.prefab, dev.name, dev.allowed_types)
        screen_surface:element({ id = "lbl_settings_temp1" .. key, type = "label",
            rect = { unit = "px", x = pos_x, y = pos_y, w = 200, h = 20 },
            props = { text = dev.desc },
            style = { font_size = 12, color = "#FFFFFF" }
        })
        screen_surface:element({
            id = "select" .. key, type = "select",
            rect = { unit = "px", x = pos_x + 200, y = pos_y, w = W - pos_x - 210, h = 20 },
            props = { options = ol, selected = sel },
            style = { bg = "#0F172A", text = "#E2E8F0", font_size = 10 },
            on_change = function(value, player)
                local id = ol[tonumber(value)+1]:match("ID:%s*(%d+)")
                local ref_dev = getDeviceByRefId(id)
                if ref_dev == nil then
                    print("Gerät mit ID " .. id .. " nicht gefunden")
                else
                    dev.prefab = ref_dev.prefab_hash
                    dev.name = ref_dev.name_hash
                    screen_surface:get("select" .. key):set_props({selected = tonumber(value)})
                end
            end,
        })
        pos_y = pos_y + 22
    end

    screen_surface:element({
        id = "btn_settings_ok", type = "button",
        rect = { unit = "px", x = pos_x, y = pos_y, w = W - pos_x - 20, h = 30 },
        style = { bg = Colors.BTN_GREEN, text = "#FFFFFF" },
        props = { text = "Fertig", z_index = 101 },
        on_click = function(playerName)
            saveSettings()
            ss.ui.activate("settings")
            
        end
    })

    screen_surface:commit()
end

Spannend ist für euch die Schleife welche in Zeile 28 beginnt. Diese durchläuft alle Geräte aus meiner globalen Liste. In Zeile 29 wird dann ein getDeviceList() ausgeführt. Dieses gibt mir einmal in ol die Objektliste zurück. Diese Liste kann ich 1:1 an den Select übergeben. Weiterhin erhalte ich unter sel den Index des Gerätes, welches aktuell in der globalen Liste ausgewählt ist (sofern dies existiert).

Zeile 30 bis 34 Erzeugt das Label mit der Beschreibung

Zeile 35 bis 51 Erzeugt das Auswahlmenü (select) dem ich unter props die ol und sel übergebe. Wenn man nun ein Objekt auswählt, wird nur ein Index über value in der on_change-Funktion ausgegeben. Daraus kann dann der ausgewählte Name ermittelt werden und in dem steckt die RefID des Gerätes. Diese wird über einen regex ausgelesen. Das alles passiert in Zeile 41 und dann landet die RefIDin id. Mit der RefID kann dann über die getDeviceByRefId Funktion nach dem Gerät gesucht werden. Ist das Gerät gefunden worden, werden der Gerätehash (prefabhash) und der Namenshash (namehash) in die globale Liste geschrieben.

Abschluss

Wie man es möchte, kann man dann auch gleich diese Liste abspeichern oder, so wie ich es im Beispiel gemacht habe, die Liste speichern, wenn man auf "Fertig" klickt. Ich hoffe, euch hat dies geholfen und es erleichtert euch ein wenig das Erstellen einer Geräteauswahl.