Zum Inhalt

Umgebung

Eine Karte in Transport Fever 3 entsteht aus einer Kette von Dateien. Das Klima (.clima.lua) nennt die Standard-Wirtschaft, die Namensliste und die Umgebung und enthält den Knotenbaum, der die Geländematerialien verteilt. Ein Geländegenerator (.gen.lua) gehört zu genau einem Klima. Er führt seinen eigenen Knotenbaum aus und erzeugt daraus die Heightmap, die Biom-Masken und die Wälder. Die Umgebung (.env.lua) legt Licht, Atmosphäre, Himmel und Wasser fest. Geländematerialien (.tmat.lua), Gras (.grass.lua), Bäume, Felsen und Tiere (.mdl mit besonderen Metadaten) liefern die Details, und die category des Klimas entscheidet, welche davon ein Spiel nutzen kann.

Das Basisspiel hat vier Klimas in base/content/climates/climates/: temperate, subarctic, tropical und dry. Jeder dieser Ordner enthält dieselben Dateien:

Datei Beispiel Zweck
.clima.lua temperate.clima.lua Klima
_clima.tree.lua temperate_clima.tree.lua Knotenbaum für die Verteilung der Geländematerialien
.gen.lua temperate.gen.lua, temperate_import.gen.lua Geländegeneratoren
_gen.tree.lua temperate_gen.tree.lua, temperate_import_gen.tree.lua Knotenbäume der Generatoren
.env.lua temperate.env.lua, temperate_old.env.lua Umgebungen

Der doppelte Ordnername kommt von den entpackten Zips (siehe Aufbau eines Mods). Im Spiel heißt das Klima ::/climates/temperate/temperate.clima.

Mods können all diese Dateien beim Laden des Spiels ändern. base/content/base/base/mod.lua hat für jeden Typ einen Modifier-Schlüssel, darunter loadClimate, loadEnvironment, loadTerrainGenerator, loadNodeTree, loadTerrainMaterial, loadGrass, loadGroundTex und loadAutoGroundTex (siehe Ressourcen-Modifier). Zur Laufzeit liegen die geladenen Ressourcen in api.res.climateRep, environmentRep, terrainMaterialTypeRep, groundTextureRep und autoGroundTexRep.

Klimas

Ein Klima ist eine .clima.lua-Datei, deren data() eine ClimateDesc zurückgibt. Die Wiki-Seite zu Klimas beschreibt die Felder. Aus base/content/climates/climates/temperate/temperate.clima.lua (gekürzt, Audio und Datenkarten folgen weiter unten):

function data()
return { 
        nodeTree = "temperate_clima.tree",
        audio = { environment = { ... } },
        dataMaps = { biomes = { ... }, layers = { ... } },
        desc = { 
            description = _("Mixed Forest Vegetation"),
            name = _("Temperate"),
        },
        category = "temperate.clima",
        economy = "/economy/temperate.eco",
        environment = "temperate.env",
        defaultNames = "/names/europe/europe.names",
        groundTextures = { 
            waterGround = "/climates/shared/gtex/water_ground.gtex",
            waterShore = "/climates/shared/gtex/water_shore.gtex",
        },
        id = "temperate",
        mapColoring = { 
            ambientColor = { 0.804, 0.859, 1, },
            levels = {
                { color = { 0.365, 0.439, 0.259, }, height = 0, },
                { color = { 0.251, 0.306, 0.196, }, height = 100, },
                { color = { 0.949, 0.922, 0.863, }, height = 550, },
            },
            sunColor = { 1, 0.961, 0.941, },
            treeColor = { 0.094, 0.141, 0.063, },
            waterColor0 = { 0.392, 0.529, 0.620, },
            waterColor1 = { 0.235, 0.314, 0.357, },
        },
        order = 10,
        skirt = { 
            colorTex = { fileName = "/climates/shared/skirt_color.dds", scaleDownAllowed = false, type = "TWOD", wrapS = "REPEAT", },
            detailTex = { fileName = "/climates/shared/skirt_detail.dds", scaleDownAllowed = false, type = "TWOD", wrapS = "REPEAT", wrapT = "REPEAT", },
            waterTex = { fileName = "/climates/shared/water_skirt.dds", scaleDownAllowed = false, type = "TWOD", },
        },
        updateScript = { fileName = "", params = { }, },
    }
end

Das ist eine Datei des Basisspiels, ihre /...-Pfade zeigen also ins Basisspiel. Ein Klima in einem Mod schreibt ::/economy/dry.eco für eine Wirtschaft des Basisspiels (Mission 07) und urbangames_campaign_mission_04::/economy/temperate.eco für eine Wirtschaft, die der Mod selbst mitbringt (Mission 04).

Feld Bedeutung
id technischer Bezeichner
desc name und description, übersetzbar. Das Wiki nennt außerdem ein icon (600×400 px bei @2-Auflösung, in Menüdialogen angezeigt). Keines der vier Basisklimas setzt eines.
order Sortierreihenfolge in den Dialogen. Die Basisklimas verwenden 10 (gemäßigt), 20 (subarktisch), 30 (tropisch) und 40 (trocken).
category das Tag, nach dem Inhalte gefiltert werden. Modelle, Geländematerialien und andere Ressourcen führen es in categoryList.categories, damit sie in diesem Klima verfügbar sind.
defaultNames, economy, environment Standard-Namensliste, Wirtschaft (.eco, siehe Wirtschaften) und Umgebung (.env) für neue Spiele
nodeTree Knotenbaum, der die Geländematerialien verteilt, siehe unten
audio.environment Umgebungsgeräusche, siehe unten
mapColoring Farben der Kartenvorschau in den Generator-Dialogen: Höhenstufen in levels (dazwischen wird interpoliert), waterColor0 (flach) und waterColor1 (tief), ambientColor, sunColor. Die Basisdateien setzen außerdem treeColor, das im Wiki fehlt.
groundTextures .gtex-Bodentexturen für den Boden unter Wasser (waterGround) und die Uferlinie (waterShore)
skirt Texturen für die Randflächen der Welt: colorTex, detailTex, waterTex
dataMaps Biome und Ebenen, die der Geländegenerator exportiert, siehe unten. Nicht im Wiki.

Das Wiki schreibt die Wassertextur des Rands als waterText, alle vier Basisklimas verwenden waterTex. Außerdem nennt das Wiki die Einträge unter skirt „Texture Maps“. In den Dateien ist jeder davon eine Texturtabelle mit fileName, type und Wrap-Modi. subarctic.clima.lua setzt zusätzlich visible = true. ClimateDesc.visible beschreibt das als Sichtbarkeit im Auswahlbildschirm.

dataMaps (ClimateDesc.DataMap) legt die Biome und Ebenen des Klimas fest. Laut API schließen Biome einander aus, während Ebenen sich mit anderen Ebenen und mit den Biomen überlappen dürfen. Das gemäßigte Klima hat fünf Biome (biome0 bis biome4) und zwei Ebenen:

dataMaps = {
    biomes = {
        { key = "biome0", displayName = _("Biome 0"), color = { 0, 0.63, 0.9 }, id = 0, },
        -- biome1 bis biome4
    },
    layers = {
        { key = "mountains", displayName = _("Mountains"), color = { 0.9, 0.5, 0.6 }, id = 1 },
        { key = "rivers", displayName = _("Rivers"), color = { 0, 0.63, 0.9 }, id = 0 },
    }
},

Die Generator-Bäume schreiben sie mit output_biomes-Knoten (type = "Biomes" oder type = "Layer" mit layerKey = "rivers" bzw. "mountains"), und die Import-Generatoren lesen sie mit input_biomes wieder ein.

Umgebungsgeräusche

Jeder Eintrag in audio.environment hat eine Liste sounds und die Eigenschaften refDist (Entfernung mit normaler Lautstärke), height (Höhe der Schallquelle über dem Boden), layer, altitudeRange (Höhenbereich der Kamera) und ambient. Das Audiosystem bevorzugt die niedrigste Ebene (layer), auf der gerade ein Geräusch spielt, und mischt die Geräusche innerhalb einer Ebene. ambient = true markiert ein Ersatzgeräusch, das spielt, wenn sonst nichts zu hören ist. Aus temperate.clima.lua:

forest = {
    sounds = { 
        { tags = {"day", "clear"}, file = "::/climates/sound/environment/nature/forest.wav" },
        { tags = {"night", "clear"}, file = "::/climates/sound/environment/nature/forest_night.wav" },
        { tags = {"day", "rain"}, file = "::/climates/sound/environment/nature/forest_rain.wav" },
        { tags = {"night", "rain"}, file = "::/climates/sound/environment/nature/forest_night_rain.wav" },
    },
    refDist = 10.0,
    height = 0.0,
    layer = 2,
},

Verwendet werden die Schlüssel water, forest, lowland, mountain, crowd, traffic, atmosphere, rain sowie residential, commercial und industrial (jeweils eine Liste mit vier Einträgen, einem pro Gebäudestufe). Das Basisklima setzt für atmosphere und rain layer = -1. Laut Wiki sind diese Geräusche immer aktiv (Wind in 4.000 m Höhe, Regen an der Kamera). Tags wählen ein Geräusch aus der Liste aus: day (6:00 bis 19:00 Uhr Spielzeit), night, clear, rain und die Epochen year1900 (1900 bis 1919), year1920 (1920 bis 1989) und year1990. Das Wiki schreibt das Regen-Tag als "rain:", die Basisklimas verwenden "rain".

terrainMasks ergänzt Geräusche für Masken, die der Geländegenerator mit einem Datenausgabe-Knoten gespeichert hat. Laut Wiki enthält es eine Liste von Structs. Die Basisdatei verwendet stattdessen eine Tabelle, deren Schlüssel der Maskenschlüssel ist:

terrainMasks = {
    river_mask = {
        sounds = { 
            { file = "::/climates/sound/environment/nature/river.wav" }
        },
        refDist = 10.0,
        height = 0.0,
        layer = 0,
        altitudeRange = { -4000.0, 0.0 },
    },
}

river_mask ist der Schlüssel eines output_map_data-Knotens in temperate_gen.tree.lua.

Verteilung der Geländematerialien

Der nodeTree des Klimas läuft nach jeder Geländeänderung und berechnet die Standard-Geländematerialien der Karte neu. Bearbeitet wird er im Terrain Generator Editor im Modus „Verteilung der Geländematerialien“ (Terrain Material Distribution). In der .tree.lua-Datei ist jeder Knoten eine Tabelle mit layerType, name, inputs, params, color und position. Die Knotennamen, die das Wiki für den Editor verwendet, entsprechen diesen Layer-Typen:

Knoten im Editor (Wiki) layerType in der Datei Parameter
Heightmap input height_map_input keine
Map data input_data keine; gibt Seed und Kartengrenzen aus
Data input input_map_data key einer Maske, die der Generator gespeichert hat
Material output material_index_output order, key, materialType, dither
Material background output material_background_index_output materialType

Laut Wiki fehlt der Ausgang für den Wasserstand am Map-data-Knoten noch. Aus temperate_clima.tree.lua der Hintergrund und ein Material:

{ 
    layerType = "material_background_index_output",
    name = "layer_background",
    params = { 
        materialType = "::/terrain/materials/grass_light_green/grass_light_green.tmat",
    },
},
{ 
    inputs = { 
        material = { key = "out", nodeName = "New Multiply map  #0", },
    },
    layerType = "material_index_output",
    name = "grass_brown",
    params = { 
        dither = true,
        key = "grass brown",
        materialType = "::/terrain/materials/grass_brown/grass_brown.tmat",
        order = 20,
    },
},

Der Eingang material ist eine Karte mit Werten von 0 bis 1, die festlegt, wo das Material gezeichnet wird. dither mischt das Material mit seinen Nachbarn. Der gemäßigte Baum hat zwölf Materialausgänge und liest vom Generator die Masken biome0 bis biome4, forest_mask, biome4_mountains und biome4_no_mountains.

Geländegeneratoren

Ein Geländegenerator ist eine .gen.lua-Datei (TerrainGeneratorDesc), die auf einen Knotenbaum verweist. Der Baum legt die Biom-Regionen fest, baut die Heightmap und setzt Bäume und Felsen (Wiki). Aus base/content/climates/climates/temperate/temperate.gen.lua (zwei von drei Parametern ausgelassen):

function data()
return { 
        nodeTree = "temperate_gen.tree",
        climate = "temperate.clima",
        desc = { 
            cargoTypeSet = { ... },
            name = _("Temperate"),
        },
        editorOnly = false,
        order = 0,
        params = {
            { 
                defaultIndex = 3,
                key = "mountains",
                name = _("Mountains"),
                tooltip = _("Adjust the number and height of mountains on the map."),
                uiType = "Slider",
                values = { _("Sparse"), _("Scattered"), _("Medium"), _("Dense"), _("Packed"), },
            },
            -- "oceans" (Seen), "water" (Flüsse)
        },
        previewSeed = "yQKiK",
        updateScript = { fileName = "", params = { }, },
    }
end

climate nennt das Klima, zu dem der Generator gehört. desc.name erscheint im Auswahlmenü, wenn ein Klima mehr als einen Generator hat, sortiert nach order. editorOnly = true blendet den Generator im Hauptmenü aus. params sind ScriptParam-Einträge, also derselbe Typ wie bei den Mod-Parametern, und der Baum liest sie über ihren Schlüssel. Weil sie in Lua geschrieben sind, zählt defaultIndex ab 1: defaultIndex = 3 wählt „Medium“ (im deutschen Spiel „Normal“). previewSeed ist der Start-Seed im Terrain Generator Editor. Das Beispiel im Wiki schreibt die Verweise mit Endung ("temperate.clima.lua", "temperate_gen.tree.lua"), die Basisdateien lassen .lua weg.

Jedes Basisklima hat außerdem einen Import-Generator. temperate_import.gen.lua unterscheidet sich von temperate.gen.lua in vier Feldern:

nodeTree = "temperate_import_gen.tree",
name = _("Temperate (Import)"),
editorOnly = true,
isImportGenerator = true,
order = 2,

TerrainGeneratorDesc.isImportGenerator kennzeichnet „a generator used when importing biomes/heightmap“, also den Generator für den Import von Biomen oder einer Heightmap. Die Import-Bäume haben keinen Heightmap-Ausgang. Sie beginnen stattdessen mit height_map_input- und input_biomes-Knoten. Das Wiki beschreibt Import-Generatoren nicht.

Knotenbaum des Generators

Knoten im Editor (Wiki) layerType in der Datei Parameter
Map data input_data keine; gibt Seed und Kartengrenzen aus
Input parameter param_number key, dummy (Platzhalterwert im Editor)
Data output output_map_data key, resolutionx, resolutiony, persistent
Heightmap output height_map_output keine; die Eingangskarte ist in Metern
Asset output assets_output Zuordnungen assets und forest

Das Wiki nennt die Auflösungsparameter „x-Resolution scale“ und „y-Resolution scale“, gemeint sind Meter pro Pixel der Maske. Die Basisbäume verwenden 32 und setzen persistent = true, was das Wiki nicht erklärt. Datenausgaben werden mit dem Spielstand gespeichert. Der Materialbaum des Klimas und die terrainMasks-Geräusche lesen sie. Aus temperate_gen.tree.lua:

{ 
    inputs = { 
        output = { key = "out", nodeName = "New Multiply map  #0", },
    },
    layerType = "output_map_data",
    name = "biome 2 mask output",
    params = { 
        key = "biome2",
        persistent = true,
        resolutionx = 32,
        resolutiony = 32,
    },
},

Die Waldmaske (forest_mask in allen Basisgeneratoren) dient auch der Wiederaufforstung: In Waldgebieten wachsen mit der Zeit wieder Bäume, nachdem der Spieler das entfernt hat, wofür sie gerodet wurden. Das Basisspiel steuert das über base/content/terrain/reforestation.gs.lua und reforestation.script.tl, die makeReforestationProposal aufrufen.

Der Asset-Ausgang ordnet den Werten zweier Karten Tags zu. Aus den Bäumen und Felsen des aktuellen Klimas, deren categoryList.categories das Tag enthält, wählt das Spiel zufällig aus. Aus temperate_gen.tree.lua:

{ 
    inputs = { 
        assets = { key = "out", nodeName = "New Constant map #275", },
        forest = { key = "out", nodeName = "add temperate river trees", },
    },
    layerType = "assets_output",
    name = "assets_out",
    params = { 
        assets = {
            { 1, "granite", },
        },
        forest = {
            { 5, "beech_weeping", },
            { 7, "temperate_conifer", },
            { 2, "shrub", },
            { 255, "temperate_single", },
            { 3, "temperate_hills", },
            { 4, "broadleaf", },
            { 6, "temperate_plains", },
            { 8, "temperate_river", },
        },
    },
},

Der gemäßigte Baum liest die Parameter oceans, water, mountains und dazu forest. forest steht nicht in den params von temperate.gen.lua, und aus den Dateien geht nicht hervor, woher sein Wert kommt. Der Knoten dahinter heißt im gemäßigten Baum „Humidity“.

Neben den eingebauten Layer-Typen kann ein Baum Skript-Knoten wie gui/node_editor/remap_number.node und gui/node_editor/split_interval.node verwenden. Sie sind in base/content/gui/gui/node_editor/*.node.lua (18 Dateien) definiert, jeweils mit Ein- und Ausgängen, einer Kategorie für den Editor und einem applyScript. Aus split_interval.node.lua:

function data()
return {
    inputs = {
        { key = "interval", type = "Point", displayName ="Interval", desc = "Interval with Lower and Upper Bound", optional = true },
        { key = "percentage", type = "Float", displayName ="Percentage", desc = "Portion of the First Part between 0 and 1", optional = true },
    },
    outputs = {
        { key = "interval_1", type = "Point", displayName ="Interval 1", desc = "Interval from Lower to Middle Bound"  },
        { key = "interval_2", type = "Point", displayName ="Interval 2", desc = "Interval from Middle to Upper Bound"  },
    },
    params = { },
    def = {
        displayName = "Split Interval",
        category = "point_feat",
        description = "Split one Interval in two Weighted Intervals",
        order = 540
    },
    withSeed = false,
    applyScript = {
        fileName = "::/gui/node_editor/layer_nodes.script@splitInterval.applyFn",
        params = {}
    },
}
end

Die Apply-Funktionen in layer_nodes.script.tl erhalten die Parameter des Knotens und eine Tabelle der Eingänge (jeder mit point, value, pointCloud oder map) und geben die Ausgänge zurück. Die Fluss- und Grat-Knoten dort nutzen climates/gen/mapgenutil.tl und rivermapgenutil.tl. Deren Typen stehen in map_gen_util_serialized, zum Beispiel RiverBuilderConfig. Ob Mods eigene .node.lua-Dateien hinzufügen können, sagen weder das Wiki noch die Dateien.

Terrain Generator Editor

Der Terrain Generator Editor ist ein Knoten-Editor im Karten-Editor. Mit ihm bearbeitest du Generator-Bäume und Bäume für die Materialverteilung (Wiki). Laut Wiki schaltest du ihn mit experimentalTerrainGeneratorEditor = true in settings.lua ein. In den Spieldateien kommt dieser Schlüssel nicht vor.

Über die Kopfleiste wechselst du zwischen den Modi „Geländegenerator“ (Terrain Generator) und „Verteilung der Geländematerialien“, öffnest und speicherst Bäume, machst Schritte rückgängig oder wiederholst sie und prüfst den Baum mit „Validieren“ (Validate) für den aktuellen Modus. Dort gibt es auch Einstellungen für den Seed, die Auflösung der Vorschau und die Parallelisierung ihrer Berechnung. Die Arbeitsfläche verschiebst du mit der rechten Maustaste oder mit W, A, S, D, das Mausrad zoomt, und R setzt die Ansicht zurück. Strg+C und Strg+V kopieren Knoten, mit Umschalt und Linksklick wählst du mehrere aus.

Die Anschlüsse sind nach Datentyp eingefärbt: blau für eine Zahl, rot für einen Punkt (zwei Zahlen), grün für eine Punktwolke (eine Liste von Punkten) und gelb für eine Karte. Manche Eingänge lassen sich als lokaler Parameter setzen, statt sie zu verbinden. Jeder Knoten kann eine Vorschau des Baums bis zu diesem Knoten erzeugen, anzeigen und als .tga exportieren. Knoten lassen sich umbenennen und umfärben. Daher stammen die Felder name und color in den .tree.lua-Dateien. Der Knotenkatalog hat die Reiter Special (Eingänge, Parameter, Ausgänge), Numbers, Points, Point Clouds, Map Generators, Noise Maps, Map Operations 1 und 2, Combine Maps sowie Ridges & Rivers.

Geländestempel

Stempel sind die Heightmaps des Werkzeugs „Gelände zerklüften“ (englisch „Roughen Terrain“) unter den Gelände-Werkzeugen im Baumenü. Mit den Generator-Bäumen haben sie nichts zu tun. Das Werkzeug gibt es nur im Karten-Editor und im Sandkastenmodus (hasHeightMapTextureTool = params.isMapEditor or params.isSandboxMode in base/content/gui/gui/main/game.tl). Jeder Stempel ist eine generische Ressource vom Typ stamp mit einer Heightmap als .png und einem .tga-Symbol daneben. base/content/gui/gui/construction/shared/tex/stamps/desert_hill1.res.lua:

function data()
    return {
        type = "stamp",
        data = {
            heightmap = resolve("desert_hill1.png"),
            icon = resolve("desert_hill1.tga")
        }
    }
end

Das Basisspiel liefert in diesem Ordner 11 Stempel aus (desert_hill1, temperate_noise, tropical_hill2 usw.). Das Werkzeug sammelt sie in construction_react_util.tl mit api.res.genericRep.getAllOfType("stamp") ein, zeigt jedes icon als Knopf und übergibt die gewählte heightmap an den Geländemodifikator. Die Tabelle data ist StampData. Eine eigene stamp-Ressource aus einem Mod müsste in derselben Liste auftauchen, weil das Werkzeug jede Ressource dieses Typs liest. Bestätigen lässt sich das nicht, denn kein ausgelieferter Mod fügt einen Stempel hinzu. Welche Größe und welches Pixelformat die Heightmap braucht, ist offen: Die .png-Dateien stehen zwar in base/_content.json, sind aber nicht entpackt.

Umgebungen

Eine Umgebung (.env.lua, Environment) legt Sonne und Mond, das Atmosphärenmodell, Wolken, Schatten, Himmelstexturen und Wasser fest (Wiki). Das Klima nennt seine Umgebung in environment. Jedes Basisklima hat eine aktuelle Umgebung und eine Variante _old, das subarktische zusätzlich subarctic2.env.lua. Für den Model Editor gibt es eigene Umgebungen in base/content/model_editor/model_editor/environments/. Aus base/content/climates/climates/temperate/temperate.env.lua (gekürzt):

function data()
return { 
        atmosphere = { 
            bottomRadius = 6360,
            atmosphereHeight = 100,
            miePhaseG = 0.8,
            mieScatteringScale = 0.00692128,
            rayScatteringCoeff = { 0.143, 0.38, 0.816, },
            rayScatteringScale = 0.0362366,
            sunIlluminance = 15.0,
            sunIntensity = 12500.0,
            sunSizeAngle = 0.9,
            cloudCoverage = 0.0,
            dayOfYear = 171,
            -- 25 weitere
        },
        light = { 
            ambientDayColor = { 0.047, 0.047, 0.047, },
            hdrContrastDay ={ 1.3, 0.95, 6.0 },
            hdrExposureDay = 1.0,
            hdrExposureNight = 2.0,
            latitude = 0.7068436145782470703125,
            refBrightness = 1.0,
            -- 18 weitere
        },
        name = _("Temperate"),
        shadows = { numCascades = 4, lambda = 0.5, ... },
        skyBox = { 
            environmentTex = { fileName = "sun.dds", scaleDownAllowed = false, type = "CUBE_MAP", },
            moonTex = { fileName = "/climates/shared/moon.tga", type = "TWOD", },
            starsTex = { fileName = "/climates/shared/stars.dds", type = "TWOD", wrapS = "MIRROR_REPEAT", wrapT = "MIRROR_REPEAT", },
            starsTexMask = { fileName = "/climates/shared/starsMask.tga", type = "TWOD", },
        },
        useSkyBoxOverwrite = false,
        water = { 
            depthColorTex = "depthColor.dds",
            extinctionCoeff = 0.05,
            normalScale = 0.4,
            normalTex = "/climates/shared/water_normal.dds",
            particleDensity = 1,
            preferLowFreq = false,
            roughness = 0.5,
            waveFoamTex = "",
        },
    }
end

Das Wiki gruppiert die Felder von atmosphere so:

Gruppe Felder Hinweise aus dem Wiki
Simulation cloudCoverage, dayOfYear, timeOfDaySec cloudCoverage von 0,0 bis 0,5, Regen ab etwa 0,45; dayOfYear 1 bis 365
Sonne und Mond sunBloom, sunBloomScale, sunIlluminance, sunIntensity, sunSizeAngle, moonIlluminance, moonIntensity sunIntensity ist das direkte Licht (0 bis 100.000), sunIlluminance das indirekte Licht (0 bis 100)
Wolken cloudBase, cloudTop, cloudSpeed, cloudPhase, cloudScattering Höhen in Metern
Mie (Dunst, Nebel) miePhaseG, mieScatteringScale, mieAbsorptionScale, mieScaleHeight trockene Klimas haben wenig Dunst
Rayleigh (blauer Himmel, rotes Abendrot) rayScatteringCoeff, rayScatteringScale, rayScaleHeight
Ozon absorptionCoeff, absorptionScale absorbiert, streut nicht
Erweitert bottomRadius, atmosphereHeight, heightOffset, fogScaleLow, fogScaleHigh Radius und Höhen in km; Nebelskalen 0,1 bis 25

Die Basisdatei setzt mehr Felder, als das Wiki aufführt: Varianten für bedeckten Himmel (mieAbsorptionScaleOvercast, miePhaseGOvercast, mieScatteringScaleOvercast), mieAbsorptionCoeff, mieScatteringCoeff, sunsetBoostScale, sunsetBoostParam, sunsetOffset, envTexScale, skylightScale und mirrorSky. Von den Wolkenfeldern setzt sie nur cloudCoverage.

In light bestimmt latitude (im Bogenmaß) die Bahn der Sonne und refBrightness die Gesamthelligkeit. Das Wiki beschreibt hdrContrastDay als eine Zahl. temperate.env.lua setzt dafür drei Zahlen, { 1.3, 0.95, 6.0 }, und hat zusätzlich Felder für Übergänge, bedeckten Himmel und Kurven (hdrExposureOvercastCurve, hdrTargetCurve, hdrMax, globalEmissiveScale und andere), die das Wiki nicht behandelt.

In water erzeugen normalTex und normalScale die Wellen (0 ergibt stilles Wasser). preferLowFreq beruhigt die Bewegung, roughness verringert die Spiegelungen, depthColorTex färbt die Tiefe, extinctionCoeff regelt die Klarheit (0 klar, 1 undurchsichtig), und eine waveFoamTex fügt animierten Schaum am Ufer hinzu. Das Wiki empfiehlt, die Werte mit den Debug-Werkzeugen im Spiel auszuprobieren.

Geländematerialien

Geländematerialien (.tmat.lua, TerrainMaterialType) färben den Boden (Wiki). Das Basisspiel hat 86 davon in base/content/terrain/materials/. Aus base/content/terrain/materials/grass_cutted_01/grass_cutted_01/grass_cutted_01.tmat.lua:

function data()
return {
    name = _("Grass Cutted Light"),

    icon = "grass_cutted_01.tga",
    previewIcon = "grass_cutted_01_preview.tga",
    detailColorTexture = "tex/grass_cutted_01_albedo.dds",
    detailMetalGlossAoHTexture = "tex/grass_cutted_01_metal_gloss_ao_h.dds",
    detailNormalTexture = "tex/grass_cutted_01_nrml.dds",
    overlayTexture = "/terrain/materials/shared/tex/overlay_0.dds",
    desc = _("Paint the ground with the materials brush."),

    grass = "grass_cutted_01.grass",

    detailSize = 1 / 32,
    overlaySize = 0.03,
    overlayStrength = 0.25,
    hOffset = 0.3,

    categories = {"grass"},
    order = 635,
    priority = 1
,   categoryList = {
        categories = { "all.clima", }
    },
}
end

Das Wiki zeigt dasselbe Material in anderer Form: die Menüdaten in einem Struct menuCategory (category = "landscaping_ground", filterCategories = {"grass"}, order = 635) und die Texte in einem Struct description. Alle 86 Basismaterialien verwenden die flache Form von oben, mit name, desc, icon, previewIcon, categories und order auf oberster Ebene. Der Referenztyp kennt beides, die flachen Felder und menuCategory.

Feld Bedeutung
detailColorTexture, detailNormalTexture Farb- und Normal-Map
detailMetalGlossAoHTexture Metall, Glanz und Ambient Occlusion; der Alphakanal ist eine Heightmap
overlayTexture, overlaySize, overlayStrength Overlay, das die Kachelung der Detailtextur aufbricht
detailSize Maßstab der Detailtextur; 48 der 86 Basismaterialien verwenden 1 / 32
hOffset wird zur Texturhöhe addiert; an der Grenze zwischen zwei Materialien ist das höhere zu sehen
priority die höchste Priorität wird obenauf gemalt (Asphalt über Erde); beim Malen von Hand ignoriert
grass .grass-Ressource mit Gras-Sprites und kleinen Pflanzen
categoryList.categories Klimas, in denen das Material verfügbar ist; 30 Basismaterialien verwenden "all.clima"

Das Wiki verlangt für die Detailtexturen 4096×4096 Pixel und rät deshalb, nur wenige zusätzliche Texturen zu verwenden. Es begrenzt außerdem name auf 32 und die Beschreibung auf 320 Zeichen und verlangt ein Symbol @2x.tga mit 240×150 px und eine Vorschau mit 720×405 px. priority ist in 33 Basismaterialien gesetzt, mit Werten von -1 (ein Material) bis 1.000.000 (die Asphalte und andere). Der Referenztyp hat hScale, aber kein hOffset. Vier Basismaterialien verwenden hOffset, keines verwendet hScale.

Gras

Eine .grass.lua-Datei setzt 2D-Gras-Sprites und kleine Pflanzenmodelle auf ein Material. Aus base/content/terrain/materials/grass_green/grass_green/grass_green.grass.lua:

function data()
return {
    colorTexture = "tex/grass_green_albedo_opacity.dds",
    metalGlossAoTexture = "/terrain/materials/grass_light_green/tex/grass_light_green_metal_gloss_ao.dds",      -- same as light green
    normalTexture = "/terrain/materials/grass_light_green/tex/grass_light_green_normal.dds",                    -- same as light green
    translucencyTexture = "tex/grass_green_translucency.dds",

    width = 1.0,
    height = 1.0,
    density = 6.0, -- stems per square meter (range allowed 0 to 16)
    lodDistance = 300.0,

    assets = {
        {
            filePath = "/assets/vegetation/plants/flower_01/flower_01.mdl",
            density = 0.08,
            scaleMin = 1.1,
            scaleMax = 1.3,
        },
        -- flower_02, flower_06
    }
}
end

width und height sind die Größe der Sprites in Metern, density ist die Zahl der Sprites pro Quadratmeter (0 bis 16) und lodDistance die Entfernung, bis zu der sie gezeichnet werden. translucencyTexture legt fest, wie viel Sonne durch dünne Teile der Pflanze scheint. Jeder Eintrag in assets setzt ein Modell mit eigener Dichte und einer zufälligen Skalierung zwischen scaleMin und scaleMax. Die Gras-Sprites lassen sich abschalten, indem du unter „Einstellungen“ > „Grafik“ (Settings > Graphics) die Option „Gras“ auf „Aus“ stellst.

Landschafts-Assets

Bäume und Felsen sind gewöhnliche Modelle mit einem zusätzlichen Metadaten-Struct: einem leeren tree = { } oder rock = { } (Wiki). Beide Arten tauchen auch im gemischten Pinsel-Werkzeug auf. Aus base/content/assets/vegetation/trees/trees/brleaf_01/brleaf_01.mdl (nur die Metadaten):

metadata = { 
    categoryList = { 
        categories = { "broadleaf", "large", "temperate_single", "temperate_hills", "town", "temperate.clima", },
    },
    description = { 
        description = _("ASSET_VEGETATION_TREES_BRLEAF_01_DESCRIPTION"),
        icon = "brleaf_01.tga",
        name = _("ASSET_VEGETATION_TREES_BRLEAF_01_NAME"),
        previewIcon = "/assets/vegetation/tmp_vegetation.tga",
    },
    emissions = { 
        noise = { },
        pollution = { idle = -0.1, },
    },
    menuCategory = { 
        categories = {
            { category = "landscaping_vegetation", filterCategories = { "tree", }, order = 200, },
        },
    },
    soundConfig = { 
        builderAudioRes = "/gui/construction/sound/buildoze_tree.builder_audio",
        soundSet = { name = "", },
    },
    tree = { },
},

categoryList.categories hat zwei Aufgaben. Der Eintrag, der zur category eines Klimas passt (temperate.clima), macht den Baum nur in diesem Klima verfügbar. Die übrigen Einträge sind die Tags, nach denen der Asset-Ausgang des Generators auswählt: broadleaf ist in temperate_gen.tree.lua der Waldwert 4, temperate_single der Wert 255. menuCategory ordnet das Objekt ins Landschaftsbau-Menü ein (ModelMetadata.MenuCategory). Die Bäume haben außerdem eine negative Verschmutzung im Leerlauf. ModelMetadata.Tree hat ein Feld reputationFactor, das town_util.tl ausliest.

Automatische Bodentexturen

Der Boden um Bäume und Felsen bekommt eine Textur, die davon abhängt, wie viele von ihnen dicht beieinanderstehen. Eine .agt.lua-Datei (AutoGroundTex) legt eine Regel fest, und ein Modell schließt sich ihr über autoGroundTex.categories an. Aus base/content/assets/vegetation/shared/shared/gtex/tree_1.agt.lua:

function data()
return {
    category = "auto.model.tree",
    gridSize = 18.0,
    countFrom = 2,
    countTo = 5,
    groundTex = "forest_floor.gtex",
    individual = true,
    individualCategory = "forestTree",
}
end

category ist "auto.model.tree" oder "auto.model.rock". Gezählt werden die Modelle in Rasterzellen von gridSize Metern, und die Textur erscheint in Zellen mit countFrom bis countTo Modellen. Mit individual = true liegt die Textur um einzelne Modelle herum (etwa die Wurzeln eines Baums) statt als Fläche zwischen ihnen. Modelle wählen sie aus, indem sie individualCategory in ihren autoGroundTex.categories nennen. Der Baum eu_lnd_01.mdl hat autoGroundTex = { categories = { "forestTree", }, }, der Felsen cr_rck_01.mdl hat "rockCrackedMedium". Die Felsregeln verwenden countTo = -1, zum Beispiel base/content/assets/rocks/shared/shared/gtex/rock_0_medium.agt.lua. Das Wiki erklärt den Wert nicht, vermutlich bedeutet er „keine Obergrenze“.

Tiere

Tiere sind Modelle mit einem Struct metadata.animal (ModelMetadata.Animal) und einer cameraConfig für die Ego-Perspektive (Wiki). Im Basisspiel liegen sie in base/content/animal/animal/, darunter wildlife_fox, wildlife_bear, wildlife_deer, wolf, cougar, bird_eagle, bird_crane, bird_gull, fish_salmon, cr_fish und Nutztiere (cow, sheep, horse). Aus base/content/animal/animal/wildlife_fox/wildlife_fox.mdl (nur die Metadaten, drei Bewegungsarten ausgelassen):

metadata = { 
    animal = { 
        config = { 
            density = 1,
            fish = false,
            idleTime = 10,
            predator = false,
            targetDistance = 64,
        },
        flockFormationFn = { fileName = "", params = { }, },
        flockName = "",
        movement = { 
            alignToTerrain = true,
            heightOffset = 0,
            rollWhileTurning = false,
            stickToWater = false,
            wobbleWhenIdle = false,
        },
        movementTypes = {
            { 
                angularSpeed = 60,
                description = _("Wandering"),
                despawnAtEnd = false,
                duration = 4,
                eventName = "walk",
                playOnce = false,
                playRandomly = true,
                playWhenSelected = false,
                speed = 1.9,
            },
            -- idle, run, sitdown (playWhenSelected), die (despawnAtEnd)
        },
        suitableAreas = { 
            bias = 5,
            height = { mapFrom = { 400, 2000, }, mapTo = { 0, -1000, }, },
            noise = { mapFrom = { 60, 150, }, mapTo = { 0, -1000, }, },
            scores = { 
                civilisation = -1000, fish = 0, forest = -4, predator = 0,
                ship = -1000, shore = -1000, water = -1000,
            },
            slope = { mapFrom = { 0.3, 2, }, mapTo = { 0, -1000, }, },
            waterDepth = { mapFrom = { 0, 1, }, mapTo = { 0, 0, }, },
        },
    },
    cameraConfig = { 
        positions = {
            { fov = 60, group = "RootNode", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, }, },
        },
    },
    categoryList = { 
        categories = { "temperate.clima", "subarctic.clima", "tropical.clima", },
    },
    description = { name = _("Fox"), },
},

config enthält density, fish (auf Wasser beschränkt), idleTime (wie lange das Tier ruht, bevor es weiterzieht), predator (andere Tiere meiden es, so wie Hirsche Bären meiden) und targetDistance (wie weit es nach einem neuen Ziel sucht). Laut Wiki haben Landtiere und Vögel die Dichte 1, Fische 4. fish_salmon.mdl hat 4, die vier tropischen cr_fish-Modelle haben 2.

movement bestimmt, wie das Tier dem Boden folgt. Landtiere haben alignToTerrain = true. Bei Fischen und Vögeln ist der Wert false, und ihr heightOffset zählt dann ab dem Wasserspiegel: negativ bei Fischen (Lachs: -1,6), positiv bei Vögeln. Fische setzen außerdem stickToWater und wobbleWhenIdle, Vögel rollWhileTurning.

Jeder Eintrag in movementTypes spielt die in eventName genannte Animation für die Dauer duration. Das Tier bewegt sich dabei mit speed (m/s) und dreht sich mit angularSpeed. Das normale Verhalten des Tiers ist eine Kette der Typen mit playRandomly = true. playWhenSelected spielt, wenn der Spieler das Tier anklickt (der Fuchs setzt sich, der Bär brüllt). playOnce beendet die Schleife, und despawnAtEnd entfernt das Tier danach, so funktioniert die. Die description erscheint im Infofenster des Tiers. Übliche Event-Namen sind idle, idle_standing, walk, fly, swim, run, die sowie Zufalls- oder Klick-Events wie roar, eat oder sitdown.

suitableAreas bewertet jeden Ort. bias ist ein fester Ausgleichswert. scores addiert feste Werte in der Nähe von Zivilisation, Fischen, Wald, Raubtieren, Schiffen, Ufern und Wasser. height, noise, slope und waterDepth bilden einen Wertebereich (mapFrom) auf einen Bereich von Punktzahlen ab (mapTo), an beiden Enden begrenzt. Ein Tier in einem negativ bewerteten Gebiet versucht, es zu verlassen, und stirbt, wenn es das nicht schafft. Der Fuchs oben meidet Wasser, Ufer, Städte und Schiffe vollständig und wird oberhalb von etwa 400 m negativ bewertet. Laut Wiki lassen sich die Bewertungskarten mit den Debug-Werkzeugen im Spiel ansehen.

Vögel und Fische können sich in Schwärmen bewegen. flockName ist der übersetzbare Name des Schwarms, und flockFormationFn verweist auf eine Funktion ohne Parameter. Sie gibt die positions der Mitglieder relativ zum Ursprung des Schwarms zurück, dazu optional overrides mit anderen Modellen für einzelne Mitglieder. Aus base/content/animal/animal/animal.script.lua:

salmon = function()
    local f = {}
    local n = math.random(5,7)
    for i=1, n do
      f[i] = { math.random(-2,2), math.random(-2,2), math.random(-3,0) }
    end
    return { positions = f }
end,

fish_salmon.mdl verweist darauf mit flockName = _("Salmon School") und fileName = "/animal/animal.script@flock.salmon". Die Funktion discus in derselben Datei mischt über overrides die vier Farbvarianten von cr_fish. Die cr_fish-Modelle setzen außerdem idForAchievement = "animal/cr_fish", sodass alle vier beim Erfolg für entdeckte Tiere als ein Tier zählen (ModelMetadata.Animal.idForAchievement). Das Beispiel im Wiki hat version = 1, die Tiermodelle des Basisspiels haben version = 2.

Offizielles Wiki: Climates, Environments, Terrain Materials, Terrain Generators, Terrain Generator Editor, Landscape Assets, Animals.