Konstruktionen¶
Bahnhöfe und andere Stationen, Depots, Industrien, Wahrzeichen, Lager, Umsteigeknoten, Signale, Haltestellen am Straßenrand, Deko-Assets und Stadtgebäude sind Konstruktionen. Jede ist eine .con.lua-Datei. Sie beschreibt die Konstruktion für das Baumenü und verweist auf eine Skriptfunktion, die die Geometrie erzeugt. Das Basisspiel hat 586 davon, verteilt auf Ordner wie depots/, industries/ und stations/.
Diese Seite behandelt die .con.lua-Datei, die Update-Funktion und ihr Ergebnis, die Konstruktionstypen, das Baumenü und Bodentexturen. Slots, Module und Vorlagen stehen auf Modulare Konstruktionen. Das offizielle Wiki beschreibt dasselbe Format auf Construction Basics.
Die zwei Hälften einer Konstruktion¶
Eine .con.lua-Datei gibt aus data() eine Beschreibungstabelle zurück. Ihre Felder entsprechen ConstructionDesc in api/tealdef/api/type.d.tl. Die Geometrie steht nicht in dieser Datei: updateScript nennt eine Funktion in einer .script.lua- oder .script.tl-Datei, und das Spiel ruft diese Funktion auf, um die eigentlichen Modelle, Spuren, Terminals und Kosten zu bekommen.
Am Helipad sieht man die Aufteilung. base/content/stations/air/air/helipad.con.lua (Beschreibung gekürzt):
function data()
return {
configureHudIconsScript = {
fileName = "::/gui/construction/construction_desc_hud_icons.script@configureStationHudIconsFn",
params = {
cargo = true,
}
},
namePrefix = _("{townName} Heliport"),
description = {
name = _("STATIONS_AIR_HELIPAD_NAME"),
description = _("STATIONS_AIR_HELIPAD_DESCRIPTION"),
icon = "helipad/helipad.tga",
previewIcon = "helipad_preview.tga",
attributes = {
noise = { 5, -1 },
pollution = { 40, -1 },
},
cargoTypeSet = {
cargoClassesIncluded = { "PASSENGERS", "UNIVERSAL", },
},
},
availability = {
yearFrom = 1950,
notificationGroup = "helicopterBuildings",
},
order = 7000,
params = {
},
updateScript = {
fileName = "helipad/helipad.script@updateFn",
params = {
}
},
menuCategory = {
categories = {
{ category = "air_buildings", order = 20000, },
},
},
}
end
Felder der Beschreibung¶
| Feld | Zweck |
|---|---|
description |
Menüeintrag: name, description, icon, previewIcon, die attributes im Infokasten und die angenommenen Güter (ModelMetadata.Description) |
availability |
yearFrom, yearTo und Einstellungen für Benachrichtigungen (ModelMetadata.Availability) |
menuCategory |
Reiter des Baumenüs, in denen die Konstruktion erscheint, siehe Baumenü |
order |
Sortierreihenfolge im Menü |
soundConfig |
soundSet.name (eine Soundset-Datei), effects (Eventname zu Sounddateien, z. B. select) und builderAudioRes, das Bau-Audioset, das beim Bauen läuft. Laut Wiki sind die Pfade relativ zur .con.lua |
params |
Parameter, die der Spieler einstellen kann, siehe Parameter |
updateScript |
Die Funktion, die das Ergebnis baut, samt den festen params, die sie mitbekommt. Das einzige Pflichtskript |
weitere ...Script-Felder |
Siehe Konstruktionsskripte |
namePrefix, subConstructionNamePrefix |
Namensvorlagen für das Fenster der gebauten Konstruktion, z. B. _("{townName} Train Depot") und "{constructionName}" |
edgeObject |
Macht die Konstruktion zu einem Kantenobjekt wie einem Signal oder einer Haltestelle am Straßenrand, siehe Kantenobjekte |
placementTag, isIndustry |
Industrien: Tag für die Wirtschaft und ob die Wirtschaft die Konstruktion platziert (Industrien) |
townBuildingParams |
Stadtgebäude: Flächennutzung, Parzellengröße und Level (Stadtgebäude) |
metadata |
Freie Tabelle; Wahrzeichen legen hier ihre Freischaltregeln ab (Wahrzeichen) |
snapping |
Assets: Flags rail, road, water zum Einrasten entlang von Gleisen, Straßen oder der Wasseroberfläche |
heightModView |
false blendet das Raster aus, das Geländeänderungen vorab zeigt; laut Wiki ist es bei sehr großen Konstruktionen meist aus |
undergroundView, heightAdjustable |
Schalten beim Bauen den „Untergrundmodus“ (Underground Mode) und die Höhenverstellung ein |
Laut Wiki bedeutet availability.yearFrom unter 1900 oder ohne Wert „ab Spielbeginn“, und yearTo ohne Wert oder 0 bedeutet kein Ende. Mit einem yearTo unter 1900 ist die Konstruktion nie verfügbar. Das Wiki nennt auch Grenzen für den Menüeintrag: name bis 32 Zeichen, description bis 320 Zeichen, icon eine @2x.tga mit 240×150, previewIcon eine .tga mit 720×405 für den Infokasten. In attributes ist jeder der Werte noise, pollution, maintenanceCost und cost ein Intervall { from, to }. Mit -1 als zweitem Wert wird nur eine Zahl angezeigt.
Die Basics-Seite des Wikis nennt zwei weitere Felder, die nicht in ConstructionDesc stehen und die keine mitgelieferte Datei verwendet: keepBuildingsAndFields (Stadtgebäude, Felder und andere Satelliten nicht abreißen) und, für Assets, buildMode. preProcessScript, das 60 .con.lua-Dateien des Basisspiels verwenden, steht weder in ConstructionDesc noch im Wiki; siehe Modulare Konstruktionen.
Parameter¶
params ist eine Liste von ConstructionScriptParam. Jeder Eintrag braucht einen key und eine Liste values. Die gewählte Option kommt in der Update-Funktion als params.<key> an.
params = {
{
key = "hangar",
name = _("Depot"),
group = "hangar",
values = { _("Yes"), _("No") },
defaultIndex = 1,
displayMode = "Horizontal",
},
},
(aus base/content/stations/air/air/airfield.tl). Dessen createTemplateFn prüft dann params.hangar == 1 für „Yes“.
Die Werte zählen ab 1: Die erste Option liefert 1, und eine Checkbox liefert 1, wenn sie nicht angehakt ist, und 2, wenn sie angehakt ist. Das Wiki weist darauf hin, dass Transport Fever 2 ab 0 gezählt hat. Alte Skripte musst du also anpassen. numbers ersetzt die Standard-Rückgabewerte 1, 2, 3, ....
| Feld | Bedeutung |
|---|---|
key |
Eindeutiger Bezeichner. Das Wiki empfiehlt Schlüssel, die über alle Mods hinweg eindeutig sind. Sonst kann ein Wert, den der Spieler bei einer anderen Konstruktion mit demselben Schlüssel gewählt hat, bei deiner vorausgewählt sein |
name, tooltip, tooltips |
Beschriftung, Tooltip des Parameters, Tooltips pro Option |
values |
Beschriftungen der Optionen; bei IconButton die Pfade von .tga-Bildern relativ zur .con.lua |
numbers |
Rückgabewerte pro Option statt 1, 2, 3, ... |
defaultIndex |
Vorausgewählte Option, ab 1 gezählt (Standard 1). In mod.json wird dagegen ab 0 gezählt, siehe Mod-Parameter |
uiType |
"Button" (Standard), "Slider", "ComboBox", "IconButton" oder "CheckBox". Auch eine Checkbox braucht zwei values, die nicht angezeigt werden |
group |
Parameter einer Gruppe sind durch eine Linie von anderen getrennt |
hideLabel |
Namen ausblenden |
location |
"Default" oder "Toolbar" (linker oder rechter Teil des Baumenüs) |
displayMode |
Das Wiki nennt "Default" und "Compact"; die Definition kennt außerdem "Horizontal", "Vertical" und "Sparse", und das Basisspiel verwendet "Horizontal" (31-mal) und "Sparse" |
yearFrom, yearTo |
Jahre, in denen der Parameter angezeigt wird |
postConstructionModifiable |
Der Parameter lässt sich nach dem Bau noch ändern |
checkEnabledScript |
Funktion, die den Parameter anzeigt, ausgraut oder ausblendet |
stepValueScript, formatValueScript |
Funktionen für eigene Schrittweiten und Wertanzeige (nur in der Definition, nicht im Wiki) |
checkEnabledScript nennt eine Funktion in einer Skriptdatei. Die Funktion gibt "Enabled", "Disabled" (ausgegraut) oder "Hidden" zurück; die Definition kennt einen vierten Wert, "InputActionOnly". Sie darf keine anderen Skript-Schnittstellen aufrufen. Laut Wiki bekommt die Funktion drei Argumente, das Spiel ruft sie aber mit zwei auf. In base/content/gui/gui/construction/construction_react_util.tl lautet der Aufruf fn(v.checkEnabledScript.params, params2). Das erste Argument ist also die params-Tabelle neben fileName, das zweite enthält die aktuellen Werte aller Parameter. Das Beispiel im Wiki verwendet sie selbst so. Aus base/content/scripts/scripts/construction/assetbuilderutil.script.lua:
checkEnabledRotationXYFn = function(captureParams, params)
return params["randomRotation"] ~= 1 and "Enabled" or "Hidden"
end,
Das Wiki erklärt die Hotkeys für die Drehung. Der Spieler dreht eine Konstruktion mit zwei Hotkey-Paaren (standardmäßig O/P und Ü/+ bzw. [/]), jeder Schritt ist 2π/32. Um sie für die Drehung um X und Y zu nutzen, fügst du param_util.makeRotationParam("constructOpt56", _("Rotation X")) und makeRotationParam("constructOpt78", _("Rotation Y")) hinzu (ParamUtil.makeRotationParam, require "::/scripts/construction/param_util.tl") und packst die Transformationen der Modelle in constructionutil.rotateTransf(params, transf) aus ::/scripts/construction/constructionutil.lua.
Konstruktionsskripte¶
Neben updateScript kann eine Konstruktion diese Funktionen nennen. Jede ist ein Skriptverweis { fileName = "file.script@function", params = { ... } }, und die Tabelle params kommt als erstes Argument in der Funktion an (captureParams).
| Feld | Aufgerufen | In .con.lua-Dateien des Basisspiels |
|---|---|---|
updateScript |
um die Konstruktion zu bauen, siehe Update-Funktion | alle |
createTemplateScript |
um die Startmodule zu setzen, siehe Vorlagen | 68 |
preProcessScript |
wenn ein Modul hinzugefügt oder entfernt wird (nicht im Wiki) | 60 |
isAffectedByToolScript |
solange ein Straßen- oder Gleiswerkzeug aktiv ist: Darf das Werkzeug diese Konstruktion ändern? | 5 |
upgradeScript |
wenn ein solches Werkzeug die Konstruktion ändert | 5 |
hudIconScript |
um das HUD-Icon der Konstruktion zu holen | 38 |
configureHudIconsScript |
welche HUD-Icons beim Platzieren angezeigt werden | 51 |
configureLayerScript |
welcher Karten-Layer angezeigt wird, während der Spieler die Konstruktion platziert | 44 |
entityWindowScript |
um das Fenster zu bauen, das bei Auswahl der Konstruktion erscheint | 38 |
Fehlt ein UI-Skript, nimmt das Spiel seinen Standard, und der kann auch leer sein.
isAffectedByToolFn(captureParams, params, conConfigAdd, conConfigRevert) gibt true zurück, wenn das Werkzeug angewendet werden darf. Ohne die Funktion gilt true. params enthält die Werte des letzten Updates, conConfigAdd das, was das Werkzeug tun will, und conConfigRevert das, was es tun würde, wenn es schon angewendet wäre. Das Wiki nennt die Schlüssel: catenary (Elektrifizierungs- und Gleiswerkzeug, 1 = aus, 2 = an), streetTemplate (Gleis- und Straßenwerkzeuge, Ressourcenname), busLane, tramTrack und tramCatenary (1 = aus, 2 = an). upgradeFn(captureParams, params, slotId, constructionConfig) bekommt dieselbe Art von Daten in constructionConfig und gibt die geänderten params zurück, die dann in die Update-Funktion gehen. slotId ist -1 bei nicht modularen Konstruktionen. Beide Funktionen dürfen api.res verwenden, aber keinen anderen Teil von api. Der Bahnhof in base/content/stations/rail/rail/modular_station/modular_station.script.lua hat beide:
isAffectedByToolFn = function(captureParams, params, conConfigAdd, conConfigRemove)
if conConfigAdd.streetTemplate then
local streetTemplate = api.res.streetTemplateRep.get(api.res.streetTemplateRep.find(conConfigAdd.streetTemplate))
return streetTemplate.roadType == 0 -- 0: "TRACK"
end
return true
end,
Die UI-Skripte im Überblick, laut Wiki:
hudIconFn(captureParams, params, userParam)gibt ein Layout zurück; Hilfsfunktionen stehen in::/gui/main/hud_icon_toolbox.tl. Die Wahrzeichen verwenden::/landmarks/landmark_hud.script@hudIconFn.configureHudIconsScriptgibt eine TabelleConstructionActionHudIconszurück:componentTypes(z. B.api.type.ComponentType.TOWN,STATION_GROUP,INDUSTRY) und mitTOWNdie FlagsshowDistricts,showTownBordersundshowPerksAndTowns. Die Standards des Basisspiels stehen in::/gui/construction/construction_desc_hud_icons.script.tl.configureLayerScriptbekommt(captureParams, layerConfig, params), wobeiparamsdie aktuellen Parameterwerte enthält, und gibt eine Layer-Konfiguration zurück. Das Baumenü ruft es auf, während der Spieler die Konstruktion platziert (base/content/gui/gui/construction/construction_react_util.tl). Das Basisspiel verwendet::/gui/construction/construction_desc_layers.script@configureDefaultLayerFn. Laut Wiki gilt der Layer auch, solange das Infofenster der Konstruktion offen ist, aber im Infofenster-Code des Basisspiels liest nichtsconfigureLayerScript.entityWindowScriptgibt eine Fensterbeschreibung zurück, meist mitrecipe = make_entity_window.ConstructionWindowaus::/gui/entity_window/make_entity_window.tl, dessenparamund einemfallbackTitle. Wie Fenster gebaut werden, steht unter GUI.
Die Update-Funktion¶
Die Funktion hinter updateScript bekommt zwei Argumente. Das erste, captureParams, ist die params-Tabelle, die in der .con-Datei bei updateScript steht. Das zweite, params, enthält die Parameterwahl des Spielers, die Module einer modularen Konstruktion und weitere Werte, die vom Konstruktionstyp abhängen. Die kleinen Bushaltestellen zeigen den ersten Teil gut. base/content/stations/street/street/small_stops/small_new.con.lua übergibt das Modell und den Radius des Einzugsgebiets:
updateScript = {
fileName = "small_stops.script@updateFn",
params = {
twoSided = false,
modelId = resolve("small_new.mdl"),
maintenanceCost = 5000,
catchmentAreaRadius = catchmentAreaRadius,
}
},
small_stops.script.lua im selben Ordner liest sie wieder aus:
function data()
return {
updateFn = function(captureParams, params)
local result = {}
result.streetTerminal = {
capacity = 50,
-- ... Gütereinstellungen ...
catchmentArea = {
position = { 0, 0, 0, },
radius = captureParams.catchmentAreaRadius,
},
passengers = true,
wantSymmetric = captureParams.twoSided,
}
result.edgeModels = {
{
model = {
id = captureParams.modelId,
transf = { -1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,0,0,1, },
}
}
}
result.cost = captureParams.buildCost or captureParams.maintenanceCost * 6
result.maintenanceCost = captureParams.maintenanceCost
return result
end
}
end
Sechs Varianten der Bushaltestelle teilen sich dieses Skript und unterscheiden sich nur in den params ihrer .con-Dateien. Die Haltestelle ist ein Kantenobjekt, darum hat ihr Ergebnis edgeModels statt Unterkonstruktionen; siehe Kantenobjekte.
Die Ergebnistabelle¶
Die zurückgegebene Tabelle ist in Teal als ConstructionUtilSerialized.ConstructionResult in base/tealdef/scripts/construction/construction_util_serialized.d.tl beschrieben. Der Kommentar in der Datei sagt, was diese Typen sind:
-- Plain lua table type definitions for construction resource scripts.
-- These types describe the format of lua tables produced by construction scripts
-- and read by C++ via converters. They must NOT reference C++ userdata types.
Die oberste Ebene (IConstructionResult) und die Felder, die das Wiki ergänzt:
| Feld | Inhalt |
|---|---|
subconstructions |
Liste von SubConstruction, siehe unten |
edgeLists |
Straßen und Gleise, siehe Kantenlisten |
edgeObjects |
Modelle wie Signale, die an diesen Kanten hängen |
snapPoint |
wie die Konstruktion beim Platzieren einrastet, siehe Snap-Punkt |
slots, slotConfig |
Modul-Slots, siehe Modulare Konstruktionen |
cost, bulldozeCost, maintenanceCost |
fällig beim Bau, beim Abriss und als laufende Kosten. Das Wiki nennt die laufenden Kosten hier monatlich und bei den Menü-attributes jährlich |
costMultiplier |
Kostenfaktor, z. B. nach Jahr (Wiki; eine mitgelieferte Datei verwendet ihn) |
noCostAtAll |
schaltet alle Kosten des Vorschlags ab, auch die der Geländeanpassung |
terminalGroups, stations |
fassen Modell-Terminals zu Stationen zusammen, siehe Stationen |
streetTerminal |
Terminal einer Haltestelle am Straßenrand (nur Kantenobjekte) |
metadata |
freie Tabelle; Wahrzeichen legen hier ihre Bauphasen ab |
Die edgeModels der Bushaltestelle und signal der Signale stehen nicht in diesen Teal-Typen. Das Wiki dokumentiert sie bei den Kantenobjekten.
Unterkonstruktionen¶
Fast alles, was in der Welt erscheint oder eine Spielfunktion hat, gehört in eine Unterkonstruktion. Die Felder laut Wiki:
| Feld | Inhalt |
|---|---|
tag |
Ganzzahl, an der das Spiel die Unterkonstruktion nach einem Update wiedererkennt |
models |
Einträge { id = "x.mdl", transf = ..., tag = "..." }; id ist relativ zur .con.lua, transf eine Matrix aus 16 Zahlen oder ein Wert aus mat4.tl |
groundFaces |
bemaltes Gelände, siehe Bodenflächen |
colliders |
Bereiche, in denen nichts anderes gebaut werden kann |
terrainAlignmentLists |
Geländeformung |
laneLists |
Wege für Personen, Güter und Fahrzeuge |
runways |
Lande- und Startbahnen für Flugzeuge und Schiffe |
labelText |
Texte für "CUSTOM"-Beschriftungen von Modellen: [modelIndex] = { "first label", "second label" } |
metadata, emissionEmitter |
freie Daten, Emissionen |
station, depot, industry, field, warehouse, maintenanceStation, townBuilding |
die Spielfunktion der Unterkonstruktion, siehe Konstruktionstypen |
stocks, rules, personCapacity, scaffold |
Güterbestände und Produktionsregeln, Arbeitsplätze oder Einwohner, Baugerüst von Stadtgebäuden |
In der Praxis enthält meist eine Unterkonstruktion das meiste davon, die übrigen nur Modelle. Manche Spielfunktionen können sich keine Unterkonstruktion teilen. Eine Konstruktion mit Station und Depot braucht deshalb für beides je eine. Klickt der Spieler die Konstruktion an, hat ihr Fenster einen Reiter pro Unterkonstruktion mit Spielfunktion.
Collider haben einen type: "BOX" oder "CYLINDER" (beide mit params.halfExtents, drei halben Abmessungen) oder "POINT_CLOUD" (mit params.points; dann wird transf ignoriert). Dazu kommt ein transf für den Mittelpunkt. collider_util_serialized beschreibt das Format, und colliderutil.createBox(...) baut einen Collider.
Listen zur Geländeanpassung haben einen type ("EQUAL" setzt das Gelände auf die Flächen, "LESS" trägt nur höheres Gelände ab, "GREATER" füllt nur tieferes Gelände auf), faces (Polygone) oder triangles (eine flache Punktliste, ein Vielfaches von drei), die Böschungsneigungen slopeLow und slopeHigh und optional = true, um Kollisionsfehler mit anderen Geländeanpassungen derselben Konstruktion zu vermeiden. Ist die Anpassung wegen etwas in der Nähe unmöglich, lässt sich die Konstruktion nicht bauen.
Spurlisten haben transf, nodes, speedLimit in m/s und transportModes. Bei nodes bilden je zwei Knoten eine Kante, jeder Knoten ist { position, tangent, width }. Laut Wiki muss die Länge der Tangente der Kantenlänge entsprechen, sonst sehen Fahrzeuge gestaucht oder gestreckt aus, und die Breite zählt nur für die Kapazität. Fahrzeugspuren kennen außerdem needsReservation und forwardOnly, Fußwege linkable (erreichbar über die automatischen kurzen Fußwegverbindungen) und pedestrianWalkPenalty. laneutil.createLane(curve, modes, speed, width, linkable) baut eine Spurliste (siehe lane_util_serialized).
Landebahnen sind { type = "LANDING" | "TAKEOFF", node = { laneList, node }, edges = { { laneList, lane }, ... } }. Am node trifft die Bahn auf die anderen Spuren. Dort hat ein Flugzeug nach der Landung abgebremst oder beginnt, für den Start zu beschleunigen. Die Richtung ergibt sich aus der Tangente der ersten oder letzten Kante. Ein erster Index von -1 verweist auf die edgeLists der Konstruktion statt auf eine Spurliste. Dieselben Indexpaare verwenden Terminals, Pools und Depots.
Bodenflächen¶
groundFaces = {
{
face = { { -10, -10, 0 }, { 10, -10, 0 }, { 10, 10, 0 } },
modes = {
{ type = "FILL", key = "industry_floor.gtex" },
},
loop = true,
},
},
face ist ein Polygon gegen den Uhrzeigersinn aus mindestens drei Punkten (z wird ignoriert). Jeder Modus hat einen type ("FILL", "STROKE", "STROKE_INNER" oder "STROKE_OUTER"), den key der Bodentextur relativ zur .con.lua und optional texCoords (eine Koordinate von 0 bis 1 pro Punkt der Fläche). loop = false lässt die Kante vom letzten zum ersten Punkt ohne Strich. alignmentOffsetMode/alignmentDirMode ("OBJECT" oder "WORLD") verschieben und drehen die Textur zusammen mit alignmentOffset/alignmentDir. Die Skripte des Basisspiels rufen meist stattdessen modulesutil.addGroundFaces("file.gtex", generatedData, subconstruction) auf.
Kantenlisten¶
Kantenlisten fügen dem Verkehrsnetz Straßen und Gleise hinzu. Gekürzt aus dem Wiki:
result.edgeLists = {
{
type = "STREET",
params = {
type = "::/infrastructure/street/country/country_new_small.street_template",
tramTrackType = "YES",
},
edges = {
{ { .0, -79.0, .0 }, { .0, 15.0, .0 } }, -- node 0
{ { .0, -64.0, .0 }, { .0, 15.0, .0 } }, -- node 1
},
snapNodes = { 0 },
freeNodes = {},
},
}
type ist "STREET" oder "TRACK". params.type nennt die Straßenvorlage (auch bei Gleisen), und tramTrackType ist "NO", "YES" oder "ELECTRIC". edgeType "BRIDGE" oder "TUNNEL" zusammen mit edgeTypeName macht eine Brücke oder einen Tunnel daraus; das Wiki empfiehlt für beide absolute Pfade. Je zwei Einträge in edges ergeben eine Kante, jeder ist { position, tangent } mit einem optionalen dritten String-Tag, der Knoten an verschiedenen Positionen zusammenführt. snapNodes sind die Knotenindizes, an die später gebaute Straßen oder Gleise anschließen können, freeNodes Knoten, die nicht zur Konstruktion gehören, und alignTerrain = true formt das Gelände wie beim normalen Straßenbau. Die Knotengruppen für Tags heißen im Wiki tag2Nodes, in der Definition und in base/content/stations/rail/rail/trainstationutil.lua aber tag2nodes. Für Ampeln beschreibt das Wiki trafficLightNodePreference (Knotenindex und "YES", "NO" oder "AUTO"). Die Definition hat stattdessen trafficLightYes und trafficLightNo, und keine mitgelieferte Konstruktion verwendet eines davon. street_util_serialized hat Hilfsfunktionen wie addStraightEdge.
result.edgeObjects hängt Modelle an diese Kanten: { edge = 1, param = .5, left = false, model = "::/stations/airport/asset/signal_runway_old.mdl" }. Kante n liegt zwischen den Knoten 2n und 2n+1, param ist der Abstand vom Startknoten in Metern, und per Skript ist nur ein Objekt pro Kante möglich.
Wie die eigenen Fahrzeugspuren einer Konstruktion an ihre Straßen anschließen, steht weder im Wiki noch in den Definitionen. Das Straßendepot zeigt, wie es das Basisspiel macht (base/content/depots/road/road/road_depot/road_depot.script.lua). Sein Straßenstück kommt aus der generierten Kurve lane1 und läuft von (0, -35, 0), dem Snap-Knoten, bis (0, -20.26, 0) an der Depotfront:
local snapNodes = { 0 }
constructionutil.addEdges(generatedData["base"].curves["lane1"], result, "STREET", { type = streetType, }, false, snapNodes, freeNodesList)
-- ...
local lane = laneutil.createLane(generatedData["base"].curves["in"], transportModes , 30, 30, true)
Die Straßenvorlage ::/infrastructure/street/constructions/entrance_new.street_template hat Spuren mit 3, 5, 5 und 3 m Breite. Die beiden Fahrspuren liegen also 2,5 m links und rechts der Straßenmitte. Die BUS/TRUCK-Spuren des Depots enden und beginnen bei (-2.5, -20.26, 0) und (2.5, -20.26, 0), genau dort, wo diese Straßenspuren enden. Vermutlich gilt: Eine Spur der Konstruktion schließt an eine Straßenspur an, wenn ihre Endpunkte an derselben Position liegen. Keine Datei sagt das, und ob das Spiel eine kleine Lücke duldet, ist offen.
Snap-Punkt¶
result.snapPoint = {
transf = transf.rotZTransl(0, vec3.new(0, -25, 0)),
snapToBaseEdgeTypes = {"STREET"},
}
transf ist der Punkt der Konstruktion unter dem Mauszeiger. transportModes und snapToBaseEdgeTypes ("TRACK", "STREET") legen fest, woran sie einrastet. allowSnapToBaseEdgeEnds erlaubt das Einrasten an Kreuzungen und Enden, alignWithCoast lässt sie an der Küste einrasten, placeAtTerrainHeight hält sie beim Einrasten auf Geländehöhe. Für Häfen gibt es forceSnapToWaterSurfaceHeight, snapHeightOffsetAboveWater und onlySnapToWaterSurfaceHeightWhenInWater. Mit allowSnapToMesh kann der Spieler mit gedrückter ALT-Taste an Modellgeometrie einrasten.
Kommentiertes Beispiel: das Helipad¶
base/content/stations/air/air/helipad/helipad.script.lua baut eine vollständige kleine Station. Die Datei beginnt mit einem Block generierter Daten, danach kommt die Update-Funktion (gekürzt, Kommentare ergänzt):
local transf = require "/scripts/mat4.tl" -- in einem Mod: require "::/scripts/mat4.tl"
local constructionutil = require "::/scripts/construction/constructionutil.lua"
local laneutil = require "::/scripts/construction/laneutil.lua"
local modulesutil = require "/scripts/construction/modulesutil.lua" -- in einem Mod: require "::/scripts/construction/modulesutil.lua"
--Begin Generated
local generatedData = {
["base"] = {
models = {
["::/assets/buildings/ind/aeration/aeration_06.mdl"] = {
{ 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -0.0, -0.0, 1.0, 0.0, 10.0, 5.18, 0.0, 1.0, },
},
-- ... eine Liste von 4x4-Transformationen pro Modell ...
--End Generated
function data()
return {
updateFn = function(captureParams, params)
local result = {}
-- eine Unterkonstruktion enthält alles; tag identifiziert sie
local stationSubconstruction = {
runways = {},
laneLists = {},
models = {},
groundFaces = {},
tag = 1000,
}
-- die generierten Modelle platzieren
constructionutil.addModelsAndGroups(params, generatedData["base"], stationSubconstruction)
-- eine Station mit einem Pool für Personen und Güter
local station = {
terminals = {},
tag = 1,
pool = { edges = {}, capacity = 100 },
catchmentAreas = {{ radius = 100 }},
}
-- Spuren: ein Fußweg für PERSON/CARGO, dann zwei HELICOPTER-Spuren
local lane = laneutil.createLane(lanes["lanes"].curves["in"], { "PERSON", "CARGO"} , 10, 5, true)
table.insert(stationSubconstruction.laneLists, lane)
-- ... zwei weitere createLane-Aufrufe für "HELICOPTER" ...
-- das Terminal verweist auf Spuren mit {Index der Spurliste, Knotenindex}
local terminal = {
passengers = true,
cargo = true,
personEdges = { { #stationSubconstruction.laneLists-3, 0}, { #stationSubconstruction.laneLists-3, 1}, },
personNodes = { { #stationSubconstruction.laneLists-3, 2}, },
vehicleNode = { #stationSubconstruction.laneLists-1, 0},
transportModes = {"HELICOPTER"},
capacity = 30,
}
table.insert(station.terminals, terminal)
-- Bahnen für Landung und Start
stationSubconstruction.runways[#stationSubconstruction.runways + 1] = {
type = "LANDING",
node = { #stationSubconstruction.laneLists-2, 0},
edges = { {#stationSubconstruction.laneLists-2, 0}, },
}
-- ... TAKEOFF-Bahn ...
stationSubconstruction.station = station
-- Bodentextur aus helipad.gtex
modulesutil.addGroundFaces( "helipad/helipad.gtex", generatedData, stationSubconstruction)
result.subconstructions = { stationSubconstruction }
-- die Station 25 m davor an einer Straße einrasten lassen
result.snapPoint = {
transf = transf.rotZTransl(0, vec3.new(0, -25, 0)),
snapToBaseEdgeTypes = {"STREET"},
}
result.cost = 180000
result.maintenanceCost = 25000
return result
end,
}
end
Die Markierungen --Begin Generated und --End Generated kommen in vielen Konstruktions- und Modulskripten des Basisspiels vor. Der Import von Konstruktionsdaten im Model Editor füllt den Block dazwischen aus einer .fbx-Datei, siehe Model Editor.
Das Helipad ist eine Datei des Basisspiels, /scripts/mat4.tl meint hier also das Basisspiel. In einem Mod zeigt / in den Mod selbst. Schreib dort ::/, wie es die Kommentare zeigen (Dateiverweise).
Konstruktionstypen¶
Eine Konstruktion hat kein Typfeld. Was sie tut, hängt von den Unterkonstruktionen in ihrem Ergebnis ab, und eine Konstruktion kann mehrere davon kombinieren. Die Wiki-Seite Construction Types beschreibt jede. Sie nennt auch einen Sonderfall: Eine einfache Konstruktion mit einer Unterkonstruktion und ohne Bodenflächen, Geländeanpassung, Station, Depot, Industrie oder Stadtgebäude wird gar nicht als Konstruktion platziert, nur als ihre Modelle. Der Spieler kann sie dann nicht anklicken, nicht nachträglich konfigurieren und nicht in einem Zug entfernen, und labelText funktioniert für sie nicht.
Stationen¶
Eine Unterkonstruktion mit einer Tabelle station ist eine Station (StationDef):
subconstruction.station = {
tag = 1,
terminals = { ... },
pool = { edges = { { 1, 1 } }, capacity = 100 },
catchmentAreas = { { position = { 0, 0, 0 }, radius = 200 } },
}
Jedes Terminal (TerminalDef) verweist mit Paaren { laneListIndex, index } auf Spuren derselben Unterkonstruktion oder mit einem ersten Wert von -1 auf die edgeLists der Konstruktion:
| Feld | Bedeutung |
|---|---|
tag |
daran erkennt das Spiel das Terminal nach einem Update wieder |
personEdges |
Wartekanten für Passagiere und Güter; ihre Länge und Breite ergeben die Kapazität. Sie müssen "PERSON" oder "CARGO" erlauben |
personNodes |
wo aussteigende Passagiere und entladene Güter erscheinen |
vehicleNode |
wo das Fahrzeug hält (vorne oder in der Mitte, je nach Fahrzeugtyp) |
vehicleEdges |
Alternative zu vehicleNode, bei der mehrere Fahrzeuge gleichzeitig laden können |
passengers, cargo |
was das Terminal abfertigt |
comfortFactor |
wie schnell wartende Passagiere ungeduldig werden |
cargoTransferSpeeds |
Liste von { cargoTypeSet, loadSpeedModifier, length } für spezialisierte Abschnitte, wichtig für gemischte Züge |
Der pool ist ein Überlauf-Wartebereich für die ganze Station. Innerhalb eines Einzugsgebiets gelten Orte als erreichbar, wenn Passagiere einen durchgehenden Fußweg haben. Andere Güter werden teleportiert.
Stationen aus Modellen, die eigene Terminals mitbringen (die Bahnsteige des Bahnhofs), gruppieren diese in result.terminalGroups. terminals listet Paare { modelIndex, terminalIndex }, und vehicleNodeOverride ersetzt den Fahrzeugknoten durch einen Knoten aus result.edgeLists. Laut Wiki landet der Fahrzeugknoten bei Straßenkanten auf der ersten Spur, und das ist meist der Gehweg. Sobald terminalGroups existiert, werden Terminals ohne Gruppe ignoriert. result.stations ordnet die Gruppen dann Stationen zu, zum Beispiel um Passagier- und Güterteil zu trennen: { { terminals = { 0, 1 }, tag = 0 }, { terminals = { 2, 3 }, tag = 1 } }. Der tag sorgt dafür, dass Linien erhalten bleiben, wenn sich die Reihenfolge ändert.
Depots¶
depot = {
transportModes = { "TRAIN", "ELECTRIC_TRAIN", },
filterTags = { "default", },
inNodes = { { 1, 0 }, },
outNodes = { { 0, 0 }, },
},
transportModes sind die Fahrzeuge, die hier verkauft werden, und filterTags beschränkt den Laden auf Fahrzeuge mit passendem Tag. inNodes und outNodes sind Ein- und Ausfahrtsknoten, an denen jeweils nur eine Spur hängen darf. Wie die Spuren des Straßendepots die Straße erreichen, steht unter Kantenlisten. Modelle des Depots können für die Tore die Animationen open und close haben. Ein Wartungsgebäude verwendet dieselbe depot-Tabelle mit catchmentArea (radius in Metern um position) und maintenancePool (wie viele Fahrzeuge gleichzeitig gewartet werden können). Fahrzeuge innerhalb des Radius werden gewartet (Depot).
Industrien¶
Eine Unterkonstruktion mit einer Tabelle industry ist eine Industrie. In der .con.lua verbindet placementTag sie mit der Wirtschaft, und isIndustry = true erlaubt der Wirtschaft, sie zum Ausgleich von Angebot und Nachfrage zu platzieren. Welche Industrien eine Karte bekommt, entscheidet die Wirtschaftsdatei des Klimas, siehe Wirtschaftssysteme. Der Güterfluss steht in stocks und rules:
subconstruction.stocks = {
{ type = "INPUT_STOCK", cargoType = "::/cargos/grain/grain.cargo", capacity = 400, loadIndicators = { 10, 11 }, tag = 1 },
}
subconstruction.rules = {
{ input = { { 8, 3 } }, output = { ["::/cargos/beverages/beverages.cargo"] = 8 }, capacity = 45, priority = 1 },
}
Der type eines Bestands ist "INPUT_STOCK", "OUTPUT_STOCK" oder "STORAGE_STOCK" (Lager). loadIndicators sind Indizes von Modellen in derselben Unterkonstruktion, die die gelagerte Menge zeigen, und tag erhält die gelagerten Güter über Updates hinweg. Das input einer Regel nennt eine Menge pro Eingangsbestand; jede innere Liste ist eine Alternative. capacity begrenzt, wie oft die Regel pro Jahr läuft. Können mehrere Regeln laufen, gewinnt die mit der höheren priority, sonst wechseln sie sich ab.
Booster: Eine Regel mit booster = true und einem boostFactor (z. B. 1.4) läuft für sich und beschleunigt die normalen Regeln, solange sie läuft. Eine personCapacity vom Typ "INDUSTRIAL" fügt Arbeitsplätze hinzu, die die Produktion ebenfalls steigern. Das Wiki empfiehlt beide Booster für Rohstoffindustrien und nur Arbeiter für verarbeitende Industrien.
Felder und Satelliten sind Module in Slots (siehe Modulare Konstruktionen). industry.maxLevel ist die Zahl der Felder im vollen Ausbau; 0 heißt keine, wie bei den meisten verarbeitenden Industrien. Die Update-Funktion bekommt dann params.sizeFactor, mit dem Skripte die Regeln skalieren, z. B. consumptionFactor = params.sizeFactor.
Lager¶
Ein Lager hat subconstruction.warehouse = { } und Lagerbestände vom Typ "STORAGE_STOCK". Diese nehmen über cargoTypes eine Menge von Güterarten an (ein Cargo-Type-Set wie bei Fahrzeugen) statt eines einzelnen cargoType. Normalerweise legt das erste eingelagerte Gut den Bestand auf diese Güterart fest. Mit mixedTypes = true bleibt er für alle angenommenen Arten offen. Laut Wiki verlängern ungemischte Bestände die Haltbarkeit gelagerter Güter. loadSpeedModifier skaliert die Ladegeschwindigkeit.
Assets¶
Assets sind Deko-Konstruktionen und können Kanten enthalten. Das Wiki nennt type = "ASSET_DEFAULT" (frei platziert) und "ASSET_TRACK" (rastet entlang von Gleisen ein, SHIFT unterdrückt das). Diese Werte entsprechen ConstructionType, aber keine mitgelieferte .con.lua setzt einen solchen type. Weitere Felder für Assets: skipCollision (Modelle dürfen sich überlappen), autoRemovable (wird entfernt, wenn etwas anderes darüber gebaut wird), categories zum Filtern und snapping. Update-Funktionen von Assets bekommen paramX und paramY (die Hotkeys), einen seed, der mit jeder Platzierung steigt, state (zwischengespeicherte Asset- und Gleisdaten) und year, dazu die eigenen Parameter dieser und früherer Asset-Konstruktionen. assetbuilderutil.makeAssetCon(...) aus ::/scripts/construction/assetbuilderutil.lua baut eine ganze Asset-Sammlung. Sieben Kampagnenmissionen liefern eine solche Datei als content/mission0Xconlua.txt mit (eine .txt, wird also vermutlich nicht als Konstruktion geladen).
Stadtgebäude¶
Die Stadt wählt Gebäude nach den townBuildingParams in der .con.lua aus: landUseType ("RESIDENTIAL", "COMMERCIAL" oder "INDUSTRIAL"), parcelSize in Schritten von 8 m ({ 1, 2 } sind 8 m entlang der Straße und 16 m Tiefe) und level 1 bis 4. Wenn die Stadt ein Gebäude platziert, bekommt die Update-Funktion zusätzlich capacity, cargoTypes, width, depth und parcelFace. Die Unterkonstruktion gibt townBuilding = { level, width, depth } zurück, eine personCapacity (type, capacity, die personNodes, an denen Menschen das Gebäude betreten) und für die Bauphase ein scaffold (Polygon buildingFace und height-Versatz unterhalb des Dachs). Der Güterbedarf ist ein INPUT_STOCK pro Eintrag in params.cargoTypes plus eine Regel, deren Eingänge Alternativen sind ({ {1,0,0}, {0,1,0}, {0,0,1} }). Eine gelieferte Güterart genügt also.
Das Basisspiel schreibt das alles über base/content/buildings/townbuildingutil.lua. Aus base/content/buildings/a/c1/1x1_01/1x1_01/a_com_l1_1x1_01.con.lua:
local townbuildingutil = require "/buildings/townbuildingutil.lua" -- in einem Mod: require "::/buildings/townbuildingutil.lua"
function data()
return townbuildingutil.makeBuildingStatic(
{ { -4.60, -0.10 }, { 4.60, -0.10 }, { 4.60, 9.10 }, { -4.60, 9.10 } },
"COMMERCIAL",
"A",
1,
{ 1, 1 },
"a_com_l1_1x1_01.script"
)
end
makeBuildingStatic(buildingFace, landUseType, era, level, parcelSize, scriptFile) setzt die Verfügbarkeit nach Epoche, die Sounds und townBuildingParams und gibt townBuildingParams und buildingFace an das Update-Skript weiter. townbuildingutil.makeBuildingScript(modelData, groundTexture) gibt eine fertige Update-Funktion zurück.
Wahrzeichen¶
Wahrzeichen sind einmalige Gebäude, die der Spieler freischaltet. Die Freischaltregel steht in den metadata der .con.lua:
perkCategory ist "LANDMARK", "HQ" (Module des Hauptquartiers), "PROSPECTION" oder "MECHANICS". Jedes Paar in rankAndPermits besteht aus einem Unternehmensrang und der Zahl der Exemplare, die auf diesem Rang freigeschaltet werden. Bauphasen und Flächeneffekte kommen in result.metadata.constructionConfig der Update-Funktion (construction_config_metadata):
result.metadata = {
constructionConfig = {
numLevels = 4,
level = 0,
underConstructionMessage = _("Supply the construction site with resources."),
requirements = { { cargoType = "::/cargos/stone/stone.cargo", amount = 800 } },
effects = { { carrier = "ROAD", type = "noise", value = 0.75 } },
},
}
numLevels zählt die Bauphasen einschließlich der fertigen. Mit level = 0 schaltet das Game-Script der Wahrzeichen die Phase weiter, sobald die requirements geliefert sind. underConstructionDescription steht bis zur Fertigstellung im Fenster. Ein Effekt hat einen carrier ("ROAD", "RAIL", "TRAM", "AIR", "WATER"), einen type ("topSpeed", "noise", "pollution", "comfort") und einen Multiplikator value für Fahrzeuge in der Nähe.
Derselbe company-Block schaltet die anderen Boni frei. Mechaniken-Boni sind Werkzeuge oder Konstruktionen mit perkCategory = "MECHANICS", etwa die Reinigungsanlage base/content/game_mechanics/game_mechanics/emission/pollution_cleanup_facility.con.lua und das Werkzeug industry_greenify_tool (siehe Werkzeuge im Baumenü). Boni der Kategorie „Prospektion“ (Prospecting) und Genehmigungsschlüssel sind allgemeine Ressourcen, keine Konstruktionen. Alle drei beschreibt die Seite Spielmechaniken.
Das Hauptquartier ist selbst eine modulare Konstruktion: base/content/landmarks/hq/hq/headquarter.con.lua mit 14 Modulen in landmarks/hq/hq/modules/, jedes mit perkCategory = "HQ" in seinem metadata.company. Das DLC-Modul hq_maze.module.lua fügt von außerhalb des Basisspiels ein fünfzehntes hinzu. Es ist das Beispiel in Eine minimale modulare Konstruktion.
Kantenobjekte¶
Signale und Haltestellen am Straßenrand sind Konstruktionen mit einer Tabelle edgeObject (ConstructionDesc.EdgeObject) in der .con.lua: snapToStreet, snapToTrack, minDistToCrossing und für Haltestellen die Vorschau-Einstellungen des Einzugsgebiets catchmentCargo, catchmentPassengers und catchmentAreaRadius. Ihre Update-Funktion bekommt zusätzlich params.baseEdgeType (NORMAL, BRIDGE oder TUNNEL) und params.roadType (STREET oder TRACK).
Das Ergebnis eines Kantenobjekts nimmt nur wenige Felder an: cost, maintenanceCost, edgeModels, signal, streetTerminal und edgeObjectEmissionEmitter (noise, pollution). Darum gibt die Bushaltestelle oben edgeModels zurück. Jeder Eintrag in edgeModels ist { edgeOffset = ..., model = { id, transf } }. Das optionale edgeOffset verschiebt das Modell entlang der Kante, und das Modell folgt dabei ihrer Kurve und Steigung. signal hat einen type ("PATH_SIGNAL" für beide Richtungen oder "ONE_WAY_PATH_SIGNAL"), optional ein soundevent und upright (true hält das Modell senkrecht, false folgt der Steigung). Ein vollständiges Signal mit seinen Animations-Events steht auf Infrastruktur.
streetTerminal (StreetTerminal) hat capacity, catchmentArea, passengers, cargoLoad, cargoUnload, cargoTransferSpeeds und wantSymmetric (dieselbe Haltestelle auf der anderen Straßenseite bauen).
Das Baumenü¶
menuCategory.categories listet die Reiter, in denen eine Konstruktion, ein Modul oder eine Metakonstruktion erscheint. Jeder Eintrag hat einen Tag category, optional filterCategories und optional order (ModelMetadata.MenuCategory):
menuCategory = {
categories = {
{ category = "rail_buildings", filterCategories = {"building"}, order = 5000 },
},
},
Das Basisspiel definiert die Reiter in base/content/gui/gui/construction/menu/menu_categories/*.res.lua (jeder mit category, order, name, icon und menu), die Straßenreiter in base/content/infrastructure/street/street/shared/category_roads_*.res.lua, die Modulreiter in .../menu/modules_categories/ und die Filter in .../menu/filter_categories/ (75 Dateien). Die Wiki-Seite Construction Menu listet die Reiter:
| Menü | Tags |
|---|---|
| Strasse (Road) | road_buildings, road_tools, road_assets |
| Schiene (Rail) | rail_buildings |
| Gleise (Tracks) | tracks, rail_tools, rail_constructions, rail_signals, rail_assets |
| Wasser (Water) | water_buildings, water_tools, water_assets |
| Luft (Air) | air_buildings, air_tools, air_assets |
| Strassen (Roads) | roads_small, roads_medium, roads_large, roads_country, roads_highway, street_constructions, roads_tools |
| Lager (Warehouses) | warehouses |
| Boni (Perks) | landmarks, prospections, perks_mechanics |
| Industrie (Industry) | industries, industries_assets (beide nur im Sandkastenmodus) |
| Landschaftsbau (Landscaping) | landscaping_terrain, landscaping_ground, landscaping_vegetation, landscaping_animals, landscaping_people, landscaping_vehicles, landscaping_assets, town_buildings_res, town_buildings_com, town_buildings_ind, town_buildings_other |
| Stadt (Town) | town_tools |
| Module (Modules) | modules_building, modules_platforms, modules_plots, modules_decoration, modules_street_access, modules_tracks, modules_landing, modules_warehouse, modules_misc |
Einige davon stammen aus den Spieldateien und fehlen in der Tabelle des Wikis: roads_tools (Menü ROADS, 17 Verwendungen), town_tools (Menü TOWN) und die neun modules_*-Reiter (Menü MODULES, verwendet von .module.lua-Dateien). Das Wiki ordnet rail_assets dem Menü „Schiene“ (Rail) zu; in der zugehörigen .res.lua steht menu = "TRACKS". Die Asset-Reiter, rail_constructions, rail_signals, die Landschaftsbau-Reiter für Tiere, Menschen, Fahrzeuge und Assets und die vier Reiter für Stadtgebäude markiert das Wiki als „mod only“. Das Basisspiel legt dort also nichts ab.
Filterkategorien grenzen einen Reiter weiter ein. Das Wiki ordnet sie nach Themen:
| Thema | Tags |
|---|---|
| Boden | asphalt, ground, gravel, grass, plant, rock, sand, misc |
| Vegetation | tree, bush, plant, flower |
| Tiere | bird, land, fish |
| Menschen | residential, worker |
| Fahrzeuge | bike, car, bus, truck, tram, train, passenger_wagon, cargo_wagon, plane, helicopter, ship |
| Gebäude | culture, sport, public, emergency, tower, lvl_1 bis lvl_4 |
| Industrien | farm, mine, factory, water |
| Infrastruktur | stop, station, depot, tracks, bridge, tunnel, intersection |
| Gleise | 600mm, 750mm, 1000mm, 1435mm, 1500mm, 3rd_rail, catenary, cogwheel, high_speed, wood, concrete |
| Brücken, Tunnel | bridge_viaduct, bridge_arch, bridge_beam, bridge_truss, bridge_suspension, tunnel_round, tunnel_rectangular |
| Sonstiges | cargo, passenger, building, barrier, construction, lamp, sign, spline, tools |
Eigene Reiter und Filter sind möglich. Das Wiki bittet Modder aber, die vordefinierten zu verwenden und sich vor dem Anlegen neuer an Urban Games zu wenden.
Werkzeuge im Baumenü¶
Nicht jeder Eintrag im Baumenü ist eine Konstruktion. Pinsel, Geländewerkzeuge, das Busspur- und das Tramgleis-Werkzeug, das Städtebau-Werkzeug und die Marketingkampagne sind Ressourcen vom Typ construction_tool: 22 .res.lua-Dateien in base/content/gui/gui/construction/tools/. Jede verweist auf eine Funktion, die den Menüeintrag zurückgibt. asset_eraser_tool.res.lua:
function data()
return {
type = "construction_tool",
data = {
getDefinitionFn = {
fileName = resolve("asset_eraser_tool.script@getDefinition"),
}
}
}
end
data ist ein ConstructionToolData. getDefinitionFn ist Pflicht. Ein optionales isDisabledFn, das true zurückgibt, lässt den Eintrag weg (kein Werkzeug des Basisspiels setzt es). Das Baumenü sammelt alle Ressourcen dieses Typs mit api.res.genericRep.getAllOfType("construction_tool") und ruft getDefinitionFn(resName, repository, params) auf (base/content/gui/gui/construction/construction_react_util.tl). Die Funktion gibt eine ConstructionDefinition zurück. Aus asset_eraser_tool.script.tl (gekürzt):
getDefinition = function(constructionDefinition : ResName, _repository : ConstructionActionRepository, _params : table) : ConstructionDefinition
return {
resName = constructionDefinition,
constructions = {},
builderAudioRes = { "::/gui/construction/sound/terraform_erase.builder_audio" },
icon = { icon = resolve("asset_reset_material.tga") },
previewIcon = { icon = resolve("asset_reset_material_preview.tga") },
name = _("Asset Eraser Tool"),
description = _("Erase assets with a brush."),
action = "ACTION_ASSET_BRUSH",
availability = { yearFrom = 0, yearTo = 0, },
-- ... hudIcons, Pinselparameter ...
menuCategory = {
categories = {
{ category = "landscaping_assets", filterCategories = {"*"}, order = -10000, },
},
},
}
end,
action wählt eines der eingebauten Werkzeuge aus ConstructionMenuAction. Ein Mod kann so ein vorhandenes Werkzeug mit anderen Einstellungen in einen anderen Reiter legen. Die Mechaniken-Boni industry_greenify_tool und marketing_campaign_tool ergänzen eine customAction mit eigenem GUI-Recipe und setzen perkCategory = "MECHANICS" in ihre metadata. Kein mitgelieferter Mod und kein DLC fügt ein construction_tool hinzu. Ob die Datei eines Mods mit diesem Typ im Menü erscheint, ist darum nicht bestätigt, auch wenn der Menü-Code jede Ressource des Typs liest.
Bodentexturen¶
Eine Bodenfläche bemalt das Gelände mit einer Bodentextur, einer .gtex.lua-Datei (710 im Basisspiel). Sie ordnet den Grauwerten eines Maskenbilds Geländematerialien zu. base/content/stations/air/air/helipad/helipad.gtex.lua (gekürzt):
local tu = require "/scripts/tex_util.tl" -- in einem Mod: require "::/scripts/tex_util.tl"
function data()
return {
texture = tu.makeMaterialIndexTexture("helipad.tga", "CLAMP_TO_EDGE", "CLAMP_TO_EDGE"),
texSize = { 36, 38 },
materialIndexMap = {
[95] = "::/terrain/materials/asphalt_02/asphalt_02.tmat",
[8] = "::/terrain/materials/asphalt_03/asphalt_03.tmat",
[199] = "::/terrain/materials/gravel_02/gravel_02.tmat",
-- ...
}
}
end
| Feld | Bedeutung |
|---|---|
texture |
die Maske, aus TexUtil.makeMaterialIndexTexture(fileName, wrapS, wrapT); die Datei ist relativ zur .gtex.lua. "CLAMP_TO_EDGE" streckt die Maske über die Fläche, "REPEAT" kachelt sie |
texSize |
Größe des Musters in Metern |
materialIndexMap |
Grauwert (1 fast schwarz bis 255 weiß) zu Geländematerial .tmat. Werte, die nicht in der Tabelle stehen, bleiben transparent und zeigen das normale Gelände |
priority |
welche Textur gewinnt, wo sich mehrere überlappen |
indices |
statt texture: data (ein String mit einer Ziffer pro Pixel) und size |
Das Beispiel im Wiki lädt ::/scripts/texutil.lua. Diese Datei gibt es noch, aber 699 der 710 .gtex.lua-Dateien des Basisspiels verwenden tex_util.tl, und keine verwendet texutil.lua. Laut Wiki sind die Materialpfade relativ zur .gtex.lua. Die Dateien des Basisspiels verwenden absolute Pfade ::/terrain/materials/.... Ein Beispiel mit einem Pixel aus base/content/buildings/shared/shared/building_paving_fill.gtex.lua:
indices = {
data = "1",
size = { 1, 1 },
},
texSize = { 8.0, 2.0 },
materialIndexMap = {
"::/terrain/materials/asphalt_03/asphalt_03.tmat",
},
priority = 10
Hilfsbibliotheken für Konstruktionen¶
base/tealdef/scripts/construction/ enthält die Definitionen der Hilfsfunktionen. Die Implementierungen sind Lua- oder Teal-Dateien in base/content/scripts/scripts/construction/.
| Definition | Inhalt |
|---|---|
| construction_util_serialized | Alle Ergebnistypen (ConstructionResult, SubConstruction, LaneList, StationDef, TerminalDef, Slot, SnapPoint, EdgeList, Stocks usw.) und addModelsAndGroups(params, group, subconstruction, tag?, transf2?) |
| lane_util_serialized | createLane(curve, transportModes, number, number, boolean), gibt eine LaneList zurück |
| street_util_serialized | Kanten-Hilfsfunktionen: addStraightEdge, addRamp, addEdge, addEdgeAutoTangents, freeAllNodes, calcScale |
| collider_util_serialized | Das Collider-Format: Typ NONE, BOX, CYLINDER oder POINT_CLOUD, transf, params.halfExtents |
| modules_util_serialized | addCosts(result, module), TransformAlignmentFaces, addGroundFaces |
| param_util | Fertige Parameter: makeTrackTypeDefaultParam, makeTrackCatenaryParam, makeTramTrackDefaultParam, makeRotationParam, getRailTrackTypes |
| asset_util | assets: Zuordnung von Asset-Gruppennamen zu Listen von .mdl-Pfaden |
| construction_config_metadata | Level, Anforderungen, Lagerbestände und Effekte in den Metadaten der Konstruktion |
| construction_specialization_metadata | Metadaten zur Spezialisierung |
Konstruktionen aus einem Mod ändern¶
Der mitgelieferte Code zeigt zwei Wege. Beim Laden bekommt addModifier("loadConstruction", fn) (und loadModule, loadMetaConstruction) die Daten jeder Datei (siehe Ressourcen-Modifier). Nach dem Laden sind api.res.constructionRep und api.res.moduleRep gewöhnliche Repositorys. Das postRunFn des Basisspiels fügt Module mit api.res.moduleRep.add(mod.fileName, mod, true) hinzu.
Um eine Konstruktion per Skript zu platzieren, gibt es api.cmd.makeWorldBuildProposalCmd und verwandte Proposal-Typen. Die Aufgabe build_construction des Missions-Frameworks prüft Vorschläge des Spielers gegen einen Konstruktions-Dateinamen (siehe Missionen). Das Format der Vorschläge ist in der api.type-Referenz dokumentiert, nicht hier.
Offizielles Wiki: Construction Basics, Construction Types, Construction Menu, Ground Textures, Modular Constructions, Construction Templates.