Zum Inhalt

Fahrzeuge

Ein Fahrzeug ist ein Modell (.mdl), dessen metadata die hier beschriebenen Fahrzeugblöcke enthält. Meshes, Materialien, LODs und die allgemeinen Metadatenblöcke (Lichter, Beschriftungen, Ladeanzeigen, Partikel) funktionieren wie bei jedem anderen Modell, siehe Modelle und Assets. Diese Seite behandelt die Metadaten von Bussen, Zügen, Schiffen und Flugzeugen, die Unterschiede zwischen den Fahrzeugtypen und Repaint-Mods. Transformator-Skripte, Zweirichtungszüge, Gruppen im Fahrzeugladen und Fake-Drehgestelle stehen auf der zweiten Seite, Fahrzeuge: fortgeschrittene Themen.

Das Wiki teilt Fahrzeuge in zwei Gruppen: Fahrzeuge, die der Spieler kauft und auf Linien einsetzt (Busse, Lkws, Trams, Züge, Schiffe, Luftfahrzeuge), und Autos, die das Spiel selbst als Individualverkehr fahren lässt.

Wo die Fahrzeugdateien liegen

Das Basisspiel legt seine Fahrzeuge im content/-Ordner unter vehicle/<type>/<name>/ ab. In den entpackten Dateien ist jedes Fahrzeug ein eigenes Archiv, deshalb steht der Fahrzeugname im Pfad doppelt: Der Bus eCitaro liegt in base/content/vehicle/bus/ecitaro/ecitaro/ecitaro.mdl, das Spiel spricht ihn als ::/vehicle/bus/ecitaro/ecitaro.mdl an (siehe Wie die Zips auf Ordner abgebildet werden). Neben dem Modell liegen mat/ (Materialien, die Texturen in mat/tex/), msh/, ani/ und icons/.

Ordner Modelle im Basisspiel Inhalt
vehicle/bus 24 Busse und Reisebusse
vehicle/truck 48 Lkws, Pferdefuhrwerke
vehicle/car 46 Privatautos (KI-Verkehr)
vehicle/tram 55 Trams, Tram-Beiwagen, Stadtbahnen
vehicle/train 70 Lokomotiven und Teile von Triebzügen
vehicle/waggon 41 Waggons ohne Antrieb (der Ordner heißt waggon)
vehicle/ship 27 Schiffe, Fähren, Luftkissenboote
vehicle/plane 30 Flugzeuge
vehicle/helicopter 11 Helikopter
vehicle/zeppelin 3 Luftschiffe, siehe unten

Das Wiki nennt für Schienenfahrzeuge ohne Motor den Ordner vehicle/wagon, im Basisspiel heißt er vehicle/waggon. Soundsets liegen in vehicle/<type>/shared/sound/, leuchtende Lichtmaterialien in vehicle/<type>/emissive/ (zum Beispiel vehicle/bus/emissive/bus_brake_lights.mtl). In vehicle/shared/ liegen die gemeinsamen Animationen, die Schmutz- und Rosttexturen und die Transformatoren für Straße und Luft. Die Transformatoren für Zug, Tram und Schiff haben jeweils ein eigenes vehicle/<type>/shared/. Die DLCs bringen 18 weitere Fahrzeugmodelle unter dlcs/<dlc>/content/vehicle/vehicle/... mit, etwa die Concorde in dlcs/urbangames_deluxe_upgrade_pack/content/vehicle/vehicle/plane/concorde/concorde.mdl.

Welche Metadatenblöcke ein Fahrzeug braucht

Block Genutzt von Zweck
transportVehicle jedem Fahrzeug, das der Spieler kaufen kann Verkehrsträger, Transportmodi, Kapazität, Beladen, Depotfilter
landVehicle Straße, Tram und Schiene Motoren, Höchstgeschwindigkeit, Gewichte, Bremsen
roadVehicle Bussen, Lkws, Autos Achsen, Lenkräder und Fake-Drehgestelle
railVehicle Zügen, Waggons, Trams Achsen und Fake-Drehgestelle
waterVehicle Schiffen Leistung, Widerstandsfläche, Wasserlinie
airVehicle Flugzeugen, Helikoptern Schub, Flügelfläche, Fahrwerk
car Privatautos kennzeichnet das Modell als KI-Verkehr
colorConfig Privatautos zufällige Lackfarben
seatProvider fast allen Positionen von Fahrer und Passagieren
draftAnimalProvider Pferdefuhrwerken, Kutschen, Pferdetrams die Tiere, die das Fahrzeug ziehen, siehe Zugtiere
emissions fast allen Lärm und Verschmutzung
soundConfig fast allen Soundset
transformatorConfig allen Skript, das Animationen und Partikel steuert
availability, cost, maintenance, description kaufbaren Fahrzeugen Jahre, Preis, Betriebskosten, Name (siehe Gemeinsame Blöcke)

In der Referenz stehen die Typen als ModelMetadata.TransportVehicle, LandVehicle, WaterVehicle und AirVehicle. roadVehicle, railVehicle, seatProvider, car und colorConfig kommen in den Modelldateien vor, haben aber keinen Teal-Record.

Alle Fahrzeuge des Basisspiels sind Modelle mit version = 2. Bei ihnen ergänzt das Basisspiel von sich aus nichts: Die Ladefunktionen, die einen Standard-transformatorConfig (addTransformatorConfig) und Standardanimationen für Türen, Räder und Lichter (createAnimationEvents*Vehicle in base/content/base/base/metadataanimationutil.lua) hinzufügen würden, laufen nur für ältere Modellversionen. Ein neues Fahrzeug muss seinen Transformator also selbst nennen und seine Animationen an den Knoten selbst mitbringen. Wie das für Brems- und Blinklichter aussieht, zeigt Lichter. Ein ganzes Modell als Ausgangspunkt findest du unter Eine vollständige Fahrzeugdatei.

Zugtiere

Pferdefuhrwerke, Kutschen und Pferdetrams werden von Tiermodellen gezogen, die das Fahrzeug in draftAnimalProvider nennt. 13 Modelle des Basisspiels nutzen den Block, darunter base/content/vehicle/bus/american_post_coach/american_post_coach/american_post_coach.mdl und die Pferdetram double_horse.mdl. Die Karnevalswagen der Kampagne machen es genauso, hier aus mods/release/urbangames_campaign_mission_01/content/vehicle/vehicle/bus/carnival_waggon_4/carnival_waggon_4.mdl (zweites Pferd gekürzt):

draftAnimalProvider = { 
    draftAnimals = {
        { 
            group = "horse_grp_l",
            model = "::/animal/draft_horse/draft_horse.mdl",
            transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.28, 0, 0, 1, },
            transfOverride = {
                { 
                    affectedGroup = "harness_btm_front",
                    attachToGroup = "harness_front_attach",
                    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                },
                { 
                    affectedGroup = "harness_btm_back",
                    attachToGroup = "harness_back_left_attach",
                    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                },
                { 
                    affectedGroup = "harness_hands",
                    attachToGroup = "harness_handle_attach",
                    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                },
            },
        },
        { group = "horse_grp_r", model = "::/animal/draft_horse/draft_horse.mdl", ... },
    },
},

Jeder Eintrag platziert ein Tier: model ist die .mdl des Tiers, group der Fahrzeugknoten, an dem es hängt (hier zwei leere Knoten horse_grp_l und horse_grp_r unter einem Knoten horse_grp vorn), und transf sein Versatz zu diesem Knoten. transfOverride setzt einzelne Knoten des Tiermodells auf Knoten des Fahrzeugs, damit das Geschirr bis zum Wagen reicht: affectedGroup ist ein Knoten in draft_horse.mdl (harness_btm_front, harness_btm_back, harness_hands), attachToGroup ein leerer Knoten im Fahrzeug (harness_front_attach unter dem Mesh des vorderen Drehgestells front_b1, harness_handle_attach in der Gruppe des Fahrersitzes back_comp_grp). Die Modelle des Basisspiels schreiben den Modellpfad als /animal/draft_horse/draft_horse.mdl. In einem Mod schreibst du wie die Kampagne ::/animal/.... Der Model Editor erzeugt diesen Block aus einem Locator draft_animal_<mdl> in der FBX-Datei, siehe Metadaten aus der FBX.

Transportfahrzeug

Aus dem eCitaro, base/content/vehicle/bus/ecitaro/ecitaro/ecitaro.mdl (gekürzt):

transportVehicle = { 
    carrier = "ROAD",
    comfortFactor = 0.65,
    compartments = {
        { 
            loadConfigs = {
                { 
                    cargoEntry = { 
                        capacity = 80,
                        cargoTypeSet = { 
                            cargoClassesIncluded = { "PASSENGERS", },
                            cargoClassesExcluded = { },
                            cargoTypesIncluded = { },
                            cargoTypesExcluded = { },
                        },
                        loadIndicator = "",
                        seats = { },
                    },
                    toHide = { },
                },
            },
        },
    },
    engineTransportModes = { "BUS", },
    entrances = {
        { path = { { 4.8, -3, 0.3, }, { 4.4, 0, 0.4, }, }, },
        { path = { { -1.1, -3, 0.3, }, { -1.1, 0, 0.4, }, }, },
    },
    filterTags = { "default", },
    loadSpeed = 5,
    transportModes = { "BUS", },
},

Transportmodi

carrier wählt den Verkehrsträger, transportModes die Netze, auf denen das Fahrzeug fahren kann, und engineTransportModes die Netze, auf denen es sich mit eigenen Motoren bewegt. Eine Elektrolok fährt auf allen Gleisen, aber mit eigener Kraft nur unter Oberleitung. Sie führt deshalb TRAIN und ELECTRIC_TRAIN als Transportmodi und nur ELECTRIC_TRAIN als Motormodus. Ein Waggon hat ein leeres engineTransportModes. Die Werte in den Dateien des Basisspiels:

carrier transportModes engineTransportModes
"ROAD" BUS oder TRUCK dieselben
"TRAM" TRAM, ELECTRIC_TRAM; Stadtbahnen zusätzlich TRAM_TRACK, ELECTRIC_TRAM_TRACK ELECTRIC_TRAM (dazu ELECTRIC_TRAM_TRACK) oder TRAM, ELECTRIC_TRAM bei Pferde- und Dampftrams
"RAIL" TRAIN, ELECTRIC_TRAIN ELECTRIC_TRAIN (elektrisch), TRAIN, ELECTRIC_TRAIN (Dampf, Diesel), leer (Waggons)
"WATER" SMALL_SHIP oder SHIP dieselben
"AIR" SMALL_AIRCRAFT, AIRCRAFT oder HELICOPTER dieselben

Stadtbahnfahrzeuge wie base/content/vehicle/tram/caf_urbos3/caf_urbos3/caf_urbos3.mdl ergänzen die Modi *_TRAM_TRACK (9 Modelle im Basisspiel). Der Fahrzeugladen (Vehicle Store) zählt solche Teile für seinen Filter „Stadtbahn“ (Light Rail) als stadtbahntauglich (base/content/gui/gui/line_vehicle_mgmt/vehicle_store_util.tl). Die Enums heißen Carrier und TransportMode.

Lässt ein Modell transportModes leer, füllt die Ladefunktion addTransportVehicleTransportModes beide Listen anhand des Verkehrsträgers. Straßenfahrzeuge bekommen BUS oder TRUCK je nach den Gütern ihrer ersten Ladekonfiguration. Schienen- und Tramfahrzeuge bekommen den rein elektrischen Motormodus, wenn alle Motoren ELECTRIC sind. Schiffe und Luftfahrzeuge bekommen den kleinen oder großen Modus aus waterVehicle.type oder airVehicle.type, Helikopter aus isHelicopter. Die Modelle des Basisspiels setzen die Listen ausdrücklich.

Weitere Felder des Transportfahrzeugs

Feld Bedeutung
loadSpeed Geschwindigkeit beim Be- und Entladen. Bei Personenwagen zählt das Wiki eine Einheit pro Türspur, die eine Person gleichzeitig benutzen kann. -1 lässt das Spiel den Wert schätzen.
comfortFactor Wie langsam die Zufriedenheit der Passagiere während der Fahrt sinkt, 0 bis 1, 0,5 ist der Durchschnitt.
priceFactor, maintenanceFactor Laut Wiki derzeit beide ungenutzt.
arrivalDelay, departureDelay Millisekunden zwischen Ankunft und Beginn des Beladens und zwischen Ende des Beladens und Abfahrt. Das Wiki empfiehlt die Länge der längsten Türanimation.
reversible Das Fahrzeug kann Teil eines Zweirichtungszugs sein, siehe Zweirichtungszüge.
filterTags Ein Depot bietet nur Fahrzeuge an, die alle seine Tags haben. Fehlt das Feld, gilt { "default" }, der Tag der Depots im Basisspiel. Eine leere Liste blendet das Fahrzeug in jedem Depot aus. Als Teil eines Triebzugs kann es trotzdem vorkommen.
multipleUnitOnly Das Fahrzeug wird nur als Teil eines Triebzugs verkauft (siehe Triebzüge).
groupFileName Übergeordneter Eintrag, unter dem Varianten im Fahrzeugladen gruppiert werden, siehe Gruppen im Fahrzeugladen.
entrances Wege, auf denen einsteigende Passagiere laufen, jeder eine Liste von Punkten relativ zum Modell. 119 Modelle des Basisspiels haben sie. Das Wiki beschreibt sie nicht.
noCblendMask true, wenn das Modell keine Farbmischmaske hat. Der Spieler kann es dann nicht umfärben.

Bei einigen dieser Felder weichen die Dateien des Basisspiels vom Wiki ab:

  • Laut Wiki berechnet das Spiel bei comfortFactor = -1 einen Wert aus den Eigenschaften des Fahrzeugs. In base/content/base/base/base_mod.lua ersetzt der erste loadModel-Modifier -1 aber durch einen festen Wert je Verkehrsträger (0,5 als Standard, 0,2 für AIR, 0,7 für WATER) und setzt dabei auch priceFactor (0,5, 0,9 für AIR, 0,1 für WATER, 0,3 für ROAD und TRAM). Kein Fahrzeug des Basisspiels nutzt -1, 115 von ihnen nutzen comfortFactor = 0.
  • Das Wiki nennt 0,5 als Standardwert von maintenanceFactor. newLoadSpeedsAndMaintenanceFactor in model_metadata_util.lua setzt ihn auf 1, wenn er fehlt.
  • Nur ein Transportfahrzeug des Basisspiels setzt arrivalDelay und departureDelay (beide 0): die Pferdetram base/content/vehicle/tram/double_horse/double_horse/double_horse.mdl. Autos setzen stattdessen arrivalDelay = 2000 in ihrem car-Block.
  • addFilterTags gibt einem Fahrzeug ohne filterTags den Tag default. Ist multipleUnitOnly gesetzt, bleibt die Liste leer.

Abteile

Das Wiki nennt die Kapazitätsliste compartmentsList. 313 Modelle aus Basisspiel und DLCs verwenden den Namen compartments, 8 DLC-Modelle compartmentsList. Die Ladefunktion toCompartmentList benennt compartmentsList in compartments um, beides funktioniert also. Sie wandelt auch die älteren Formen capacities und loadConfigs um.

Die Struktur hat drei Ebenen:

  • Eine Liste von Abteilen. Die meisten Fahrzeuge haben eines. Ein Fahrzeug, das zwei Ladungen gleichzeitig befördert, hat ein Abteil pro Ladung. Das Beispiel im Wiki ist eine Doppelstocktram mit Passagieren oben und Gütern unten.
  • Jedes Abteil hat loadConfigs, die möglichen Arten, es zu füllen. Ein Schiff, das entweder nur Kohle oder je zur Hälfte Kohle und Eisenerz befördert, braucht zwei Ladekonfigurationen. Lädt ein leeres Fahrzeug, wählt das Spiel die erste Konfiguration, die unter dem Güterfilter der Linie die meisten Güter befördert.
  • Jede Ladekonfiguration hat einen cargoEntry (capacity, cargoTypeSet, loadIndicator, seats) und toHide, eine Liste von Knotennamen, die ausgeblendet werden, solange die Konfiguration aktiv ist.

cargoTypeSet listet eingeschlossene und ausgeschlossene Güterklassen und Gütertypen auf (siehe Gütertyp-Sets). loadIndicator nennt einen Eintrag im Block loadIndicator des Modells (Ladeanzeigen). seats listet die Indizes der Sitzplätze, die zu diesem Eintrag gehören. Das ist für frühe Schiffe wichtig, die Passagiere und Güter im selben Rumpf befördern.

Emissionen

emissions = { 
    noise = { score = 15, },
    pollution = { score = 15, },
},

score reicht von 0 bis 100 und bewertet das Fahrzeug innerhalb seines Verkehrsträgers. Das Spiel rechnet ihn je Verkehrsträger in echte Werte um und füllt idle (im Stand), speed (bei Höchstgeschwindigkeit) und power (bei vollem Schub), dazwischen wird interpoliert. Mit -1 schätzt das Spiel den Wert aus den übrigen Daten des Fahrzeugs. Ein Fahrzeug ohne emissions bekommt für beide score = -1 (addMissingVehicleStats in model_metadata_util.lua). Das Wiki empfiehlt mehr Lärm und Verschmutzung für Fahrzeuge mit mehr Leistung und Geschwindigkeit, damit jedes Fahrzeug einen Nachteil hat.

Sitzplätze

seatProvider = { 
    crewModels = { },
    drivingLicense = "BUS",
    seats = {
        { 
            animation = "driving_upright",
            crew = true,
            group = "comp",
            transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4.63, 0.79, 0.435, 1, },
        },
        { animation = "sitting", group = "comp", transf = { ... }, },
        -- ...
    },
},

drivingLicense wählt die Standardmodelle für die Besatzung. Das Wiki nennt BUS, TRUCK, TRAM, RAIL, WATER, AIR und AIR_OUTDOOR (Pilot mit Helm). Die Helikopter des Basisspiels nutzen einen weiteren Wert, HELICOPTER (12 Modelle aus Basisspiel und DLCs, z. B. base/content/vehicle/helicopter/bell_47/bell_47/bell_47.mdl). crewModels ersetzt die Standardmodelle durch eigene Figurenmodelle. Besatzungsmodelle sind Figurenmodelle, deren person.drivingLicenses die Lizenz nennt, siehe Personen.

Jeder Sitzplatz hat eine animation (driving, driving_upright, idle, sitting oder walk), crew (die Besatzung ist auch ohne Passagiere an Bord sichtbar; Standard ist false), group (der Knoten, mit dem sich der Sitzplatz bewegt und ausgeblendet wird) und transf. forward beschränkt einen Besatzungsplatz in Zweirichtungszügen auf eine Fahrtrichtung. Ein Sitzplatz an einem ausgeblendeten Knoten ist ebenfalls ausgeblendet. So bekommt eine zweite Lokomotive keinen Lokführer.

Sound

soundConfig = { 
    soundSet = { name = "/vehicle/bus/shared/sound/bus_electric.snd", },
},

soundSet.name verweist auf ein Soundset (.snd, siehe Soundsets). Das Basisspiel schreibt für seine eigenen Sets /vehicle/.... In einem Mod stellst du den Sets des Basisspiels :: voran, zum Beispiel ::/vehicle/bus/shared/sound/bus_electric.snd. effects überschreibt einzelne Ereignisse dieses Sets. Das Beispiel im Wiki schreibt das als "horn" = "audio/horn.wav", was kein gültiges Lua ist. Der Typ (SoundConfig.effects) und die Zeppeline (base/content/vehicle/zeppelin/zeppelin_nt/zeppelin_nt/zeppelin_nt.mdl) verwenden eine Liste von Dateien pro Ereignis:

effects = {
    select = { "sound/selected_zeppelin.wav" },
},

Autos

Ein car-Block macht aus einem Straßenmodell ein Auto des Individualverkehrs. Das Wiki zeigt ein leeres car = { }. Alle 48 Autos aus Basisspiel und DLCs setzen darin zwei Werte, hier aus base/content/vehicle/car/beetle/beetle/beetle.mdl:

car = { 
    arrivalDelay = 2000,
    spawnWeight = 1,
},
colorConfig = { 
    configs = {
        { { 0.601961, 0.572635, 0.496913, }, },
        { { 0.553856, 0.633333, 0.623564, }, },
        -- ...
    },
},

spawnWeight ist bei den meisten Autos 1 und bei seltenen niedriger (bis hinunter zu 0,0001). Das Wiki erwähnt es nicht. Ein Auto hat wie ein Bus landVehicle, roadVehicle, seatProvider und emissions, aber kein transportVehicle. colorConfig.configs listet Farben (RGB, 0 bis 1), aus denen das Spiel zufällig wählt. Das Material des Autos braucht dafür eine Farbmischmaske.

Als Richtwert für das Balancing nennt das Wiki die Höchstgeschwindigkeiten der Autos im Basisspiel nach Epoche: 20 km/h bis 1925, 50 km/h bis 1950, 80 km/h bis 1980, 100 km/h bis 2000 und danach 120 km/h. topSpeed ist in m/s angegeben, teile also durch 3,6.

Fahrzeugtypen

In TF3 benennen die meisten typspezifischen Metadaten Teile des Modells über ihren Knotennamen. Gib den Knoten eindeutige Namen. Der Model Editor zeigt sie im Knotenbaum und kopiert einen Namen per Doppelklick.

Landfahrzeuge

Straßen-, Tram- und Schienenfahrzeuge teilen sich einen landVehicle-Block. Aus dem eCitaro:

landVehicle = { 
    curveSpeedScale = 1,
    engines = {
        { power = 150, tractiveEffort = 30, type = "ELECTRIC", },
    },
    topSpeed = 19.444,
    weightEmpty = 14000,
    weightMaxPayload = 15429,
},
Feld Bedeutung
engines null oder mehr Motoren, jeder mit type (HORSE, STEAM, DIESEL, ELECTRIC), power in kW und tractiveEffort in kN
topSpeed m/s
weightEmpty, weightMaxPayload kg; die Zuladung ist das zusätzliche Gewicht bei voller Beladung
friction Reibungskoeffizient. Das Wiki nennt ihn zusammen mit brakeDeceleration als Stellschraube für Beschleunigen und Bremsen. Alle Züge und Trams des Basisspiels setzen 0,02, Straßenfahrzeuge lassen ihn weg.
brakeDeceleration wie stark das Fahrzeug bremst. Alle Züge und Trams des Basisspiels setzen 2,5, Straßenfahrzeuge lassen den Wert weg. Modelldateien und Wiki nennen keine Einheit. Der Laufzeitwert VehicleData.brakeDeceleration ist in m/s² dokumentiert.
curveSpeedScale in 44 Modellen des Basisspiels gesetzt, auf 1 oder bei den Teilen der Neigezüge Avelia Liberty und ETR 450 auf 1,25. Das Wiki beschreibt das Feld nicht.

Der Teal-Record ModelMetadata.LandVehicle kennt weder brakeDeceleration noch curveSpeedScale. In Teal liest und schreibst du sie über eine einfache Tabelle, zum Beispiel (model.metadata.landVehicle as {string:any}).brakeDeceleration.

Das Wiki zählt Geschwindigkeit und Gewicht zu den Eigenschaften von roadVehicle und railVehicle. Die Modelle des Basisspiels führen sie in landVehicle. turnRoadAndRailToLandVehicle baut einen landVehicle aus einem älteren roadVehicle oder railVehicle, wenn das Modell keinen hat. Die Standardwerte sind dann eine Bremsverzögerung von 5 (Straße) oder 2,5 (Schiene) und eine Reibung von 0,1 oder 0,02.

Straßenfahrzeuge

roadVehicle.config benennt die beweglichen Teile. Aus dem eCitaro (eine Liste von Fake-Drehgestellen pro LOD):

roadVehicle = { 
    config = { 
        axles = { "w2", },
        fakeBogies = {
            { { group = "RootNode", offset = 0, position = 0, }, },        -- LOD 0
            { { group = "RootNode", offset = 0, position = 0, }, },        -- LOD 1
            { { group = "RootNode", offset = 0, position = 0, },
              { group = "RootNode", offset = 0, position = -2.63, }, },    -- LOD 2
        },
        steeringParts = { },
        wheels = { "w1_lft", "w1_rgt", },
    },
},

axles sind Knoten, die nur rollen (Drehung um die y-Achse), meist die Hinterachsen. wheels rollen und lenken (Drehung auch um die z-Achse), meist die Vorderräder. steeringParts lenken nur. Fake-Drehgestelle (fakeBogies) erklärt die Seite Fahrzeuge: fortgeschrittene Themen. Ein Straßenfahrzeug hat meist eines zwischen der Lenkachse und der ersten Hinterachse. Lege den Mesh-Ursprung einer Achse auf der x- und z-Achse in ihre Mitte, sonst eiert sie im Spiel.

Schienenfahrzeuge

railVehicle.config hat axles und fakeBogies. Einfache Schienenfahrzeuge brauchen Fake-Drehgestelle nur in LODs ohne eigene Achsen. Gelenktrams und Züge mit Jakobs-Drehgestellen brauchen sie in mehr LODs. Die Tram Be 4/6, base/content/vehicle/tram/be4_6mirage/be4_6mirage/be4_6mirage.mdl (Anfang von LOD 0):

railVehicle = { 
    config = { 
        axles = { "front_w1", "front_w2", "middle_w1", "middle_w2", "back_w1", "back_w2", },
        fakeBogies = {
            {
                { group = "front_grp", offset = 0, position = 2.5, },
                { group = "back_grp", offset = 0, position = -1.81, },
                { group = "middle_grp", offset = 0, position = 2.5, },
                { group = "middle_grp", offset = 0, position = -1.5, },
                -- ...
            },
            -- weitere LODs
        },
    },
},

Trams und Züge unterscheiden sich durch carrier (TRAM oder RAIL) und ihren Transformator (default_tram.trf oder default_train.trf).

Triebzüge

Ein Triebzug ist ein fester Zugverband, der als ein Fahrzeug verkauft wird. Er ist eine .mu.lua-Datei neben seinen Modellen, das Basisspiel hat 13 davon. Aus base/content/vehicle/train/emd_f/emd_f/emd_f.mu.lua:

function data()
    return {
        vehicles = {
            { name = "emd_fa.mdl", forward = true },
            { name = "emd_fb.mdl", forward = true },
        },
        name = _("VEHICLE_MULTIPLEUNIT_EMD_F_NAME"),
        desc = _("VEHICLE_MULTIPLEUNIT_EMD_F_DESCRIPTION"),
        filterTags = { "default" },
    }
end

vehicles listet die Modelle relativ zur .mu.lua-Datei. forward = false dreht das Modell um 180 Grad, so wie der ICE 1 seinen hinteren Triebkopf in base/content/vehicle/train/ice1/ice1/ice1.mu.lua. name und desc erscheinen im Fahrzeugladen (für die Übersetzung in _() eingeschlossen). Die technischen Daten berechnet das Spiel aus den Teilen. filterTags und ein optionales groupFileName funktionieren wie bei einem einzelnen Fahrzeug. Ein Triebzug ist nur verfügbar, solange alle seine Teile innerhalb ihrer Verfügbarkeitsjahre liegen.

Die Teile der Triebzüge im Basisspiel setzen in ihrem transportVehicle multipleUnitOnly = true und filterTags = { } (31 Modelle, z. B. ice1_front.mdl). Einzeln werden sie deshalb nicht verkauft.

Wasserfahrzeuge

Schiffe haben einen waterVehicle-Block. Aus base/content/vehicle/ship/kakadua/kakadua/kakadua.mdl (Wasserlinie gekürzt):

waterVehicle = { 
    area = 5,
    availPower = 600000,
    maxRpm = 133,
    topSpeed = 10.5556,
    waterLine = {
        { 11.8805, 0, },
        { 11.8805, 0.125, },
        -- ... 31 Punkte entlang einer Rumpfseite
        { -10.96, 0, },
    },
    weightEmpty = 125000,
    weightMaxPayload = 18000,
},
Feld Bedeutung
area größter Unterwasserquerschnitt des Rumpfs in m²; der Widerstand wächst linear damit
availPower Motorleistung in W (nicht kW)
weightEmpty, weightMaxPayload kg
maxRpm Höchstdrehzahl von Schaufelrad oder Propeller, für die Animation
topSpeed m/s
type "SMALL"-Schiffe können kleine Anleger nutzen, "BIG"-Schiffe brauchen den großen Hafen
waterLine x/y-Punkte des Rumpfumrisses auf Höhe der Wasserlinie, für den Schaum um das Schiff

Die Schiffe des Basisspiels setzen type nicht. Sie geben die Größe über transportModes = { "SMALL_SHIP" } oder { "SHIP" } an, die die Ladefunktion sonst aus type ableiten würde. Nur das DLC-Schiff ts_queen_mary.mdl setzt type.

Luftfahrzeuge

Flugzeuge und Helikopter haben einen airVehicle-Block. Aus base/content/vehicle/plane/douglas_dc3/douglas_dc3/douglas_dc3.mdl (gekürzt):

airVehicle = { 
    axles = {
        { position = { 3.38, 0.59, }, radius = 0.59, },
        { position = { 3.38, 0.59, }, radius = 0.59, },
    },
    config = { 
        axleRadii = { 0.59, 0.59, },
        axles = { "w1_lft", "w1_rgt", },
        fakeBogies = { ... },
        steeringParts = { "g2", },
        wheelRadii = { 0.29, },
        wheels = { "w3", },
    },
    hasFlaps = true,
    maxThrust = 25000,
    timeToFullThrust = 3,
    topSpeed = 83.333,
    weightEmpty = 7700,
    wheels = {
        { position = { -7.81, 2.56535, }, radius = 0.27, },
    },
    wingArea = 91,
},

Die äußeren Listen axles und wheels geben Position (x und z vom Ursprung) und Radius jeder Achse und jedes Rads an. Damit kann das Spiel das Flugzeug am Boden platzieren und neigen. config benennt die Knoten wie bei Straßenfahrzeugen, axleRadii und wheelRadii geben je aufgeführtem Knoten einen Radius in Metern an. Die Flugwerte sind maxThrust und idleThrust in N, timeToFullThrust in Sekunden, topSpeed in m/s, weightEmpty und weightMaxPayload in kg, wingArea in m² (der Auftrieb wächst linear damit), hasFlaps, isHelicopter für senkrechten Start und Landung und type ("SMALL" für kleine Flugplätze, "BIG" für Flughäfen).

Wie die Schiffe geben die Flugzeuge des Basisspiels ihre Größe über transportModes an (SMALL_AIRCRAFT, AIRCRAFT, HELICOPTER) und lassen type weg. Die Concorde setzt außerdem maxTakeOffWeight = 54400, ein Feld, das im Wiki fehlt. Helikopter wie die Bell 47 setzen isHelicopter = true, transportModes = { "HELICOPTER" } und drivingLicense = "HELICOPTER".

Die drei Modelle in base/content/vehicle/zeppelin/ sind keine Transportfahrzeuge. Sie haben weder transportVehicle noch airVehicle, und ihr eigener Transformator vehicle/zeppelin/shared/zeppelin.trf bewegt sie.

Animationsereignisse nach Typ

Mit den Standard-Transformatoren laufen Animationen, die nach diesen Ereignissen benannt sind, von selbst ab. Die Zeiten für rudder, aileron_* und elevator laufen von 0 bis 1000, 500 ist die Mittelstellung. flaps läuft von 0 (eingefahren) bis 1000 (ausgefahren).

Ereignis Straße Tram Schiene Wasser Luft
forever (in Schleife, z. B. Lüfter, Radar) ja ja ja ja ja
drive (folgt der Geschwindigkeit) ja ja ja ja ja
wheels (Länge entspricht einer Radumdrehung) ja
open_all_doors, close_all_doors ja ja ja ja ja
open_doors_left/_right, close_doors_left/_right ja ja ja ja ja
brake_lights_on/_off ja ja ja
blink_lights_left_on/_off, blink_lights_right_on/_off ja ja
paddles, rudder ja rudder
aileron_left, aileron_right, elevator, flaps ja
beacon_lights, strobe_lights, landing_light_on/_off ja
props_on/_off, props_blurred_on/_off ja
open_doors_cargo, close_doors_cargo, open_wheels, close_wheels ja

Die Türereignisse werden ausgelöst, nachdem das Fahrzeug hält und bevor es abfährt, die Varianten für links und rechts nur bei Halten auf dieser Seite. Drehe bei Schiffspropellern das Mesh um 90 Grad, damit seine y-Achse entlang der Drehachse zeigt. Zweirichtungszüge haben weitere Ereignisse zum Ein- und Ausblenden, siehe Zweirichtungszüge. Der eCitaro legt seine Türanimationen als Dateien pro Ereignis ab, z. B. ani/open_doors_right/d1.ani.

Lichter

Fahrzeuglichter sind Meshes mit einem leuchtenden Material, und die Lichtereignisse schalten sie ein und aus. Lege jede Lichtgruppe (Scheinwerfer, Bremslichter, Blinker links und rechts) auf einen eigenen Knoten mit eigenem Mesh und gib ihr ein leuchtendes Material aus vehicle/<type>/emissive/. Im Bus-Ordner liegen bus_all_lights.mtl, bus_add_lights.mtl, bus_brake_lights.mtl und bus_blink_lights.mtl. Auto, Lkw und Tram haben passende Dateien, der Zug hat train_all_lights.mtl und train_red_lights.mtl. Leuchtet ein Licht nur zeitweise, hängst du beide Animationen des Ereignispaars an seinen Knoten, jeweils als FILE_REF auf die gemeinsame Datei in vehicle/shared/ani/ (brake_lights_on.ani und brake_lights_off.ani, blink_lights_left_on.ani usw.). Die Bremslichter des eCitaro aus base/content/vehicle/bus/ecitaro/ecitaro/ecitaro.mdl:

{ 
    animations = { 
        brake_lights_off = { 
            params = { id = "/vehicle/shared/ani/brake_lights_off.ani", },  -- in einem Mod: "::/vehicle/shared/ani/brake_lights_off.ani"
            type = "FILE_REF",
        },
        brake_lights_on = { 
            params = { id = "/vehicle/shared/ani/brake_lights_on.ani", },   -- in einem Mod: "::/vehicle/shared/ani/brake_lights_on.ani"
            type = "FILE_REF",
        },
    },
    materials = { "/vehicle/bus/emissive/bus_brake_lights.mtl", },     -- in einem Mod: "::/vehicle/bus/emissive/..."
    mesh = "msh/brakelights_lod0.msh",
    name = "brakelights",
    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
},

Die Knoten blinklights_lft und blinklights_rgt folgen demselben Muster mit bus_blink_lights.mtl und den Paaren blink_lights_left_* und blink_lights_right_*. Die Scheinwerfer (headlights, mit bus_all_lights.mtl) haben keine Animation. Den Lichtkegel auf der Straße erzeugt der Block lightSourceList, dessen Einträge den Knoten in meshId nennen, siehe Lichtquellen. Züge schalten ihre Spitzen- und Schlusslichter stattdessen nach der Position im Zugverband, siehe Meshes nach Position ausblenden.

Eine vollständige Fahrzeugdatei

Diese Seite zeigt den eCitaro stückweise. Als Vorlage für ein neues Fahrzeug in einem Mod eignet sich eine ganze Datei aus einem Mod besser, weil sie schon die Pfade verwendet, die ein Mod braucht. Der vierte Karnevalswagen der Kampagne, mods/release/urbangames_campaign_mission_01/content/vehicle/vehicle/bus/carnival_waggon_4/carnival_waggon_4.mdl, ist ein von Pferden gezogener Bus mit allen Fahrzeugblöcken eines Straßenfahrzeugs. Er leiht sich mit ::/-Pfaden Meshes und Materialien der American Post Coach aus dem Basisspiel und holt eigene Dekorationen über /-Pfade aus dem Kampagnen-Mod. Gekürzt auf ein LOD mit wenigen Knoten, Zahlen gerundet:

function data()
return { 
    boundingInfo = { bbMax = { 4.898, 1.09, 3.395, }, bbMin = { -2.22, -1.09, -0.002, }, },
    collider = { 
        params = { halfExtents = { 3.559, 1.09, 1.698, }, },
        transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
        type = "BOX",
    },
    lods = {
        { 
            node = { 
                children = {
                    { 
                        children = {
                            { 
                                children = {
                                    { name = "harness_front_attach", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3.4, 0, 0.85, 1, }, },
                                    -- harness_back_left_attach, harness_back_right_attach
                                },
                                materials = { "::/vehicle/bus/american_post_coach/mat/american_post_coach.mtl", },
                                mesh = "::/vehicle/bus/american_post_coach/msh/front_b1_lod0.msh",
                                name = "front_b1",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                            },
                            { 
                                materials = { "::/vehicle/bus/american_post_coach/mat/american_post_coach.mtl", },
                                mesh = "::/vehicle/bus/american_post_coach/msh/front_w1_lod0.msh",
                                name = "front_w1",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.085, 0, 0.625, 1, },
                            },
                        },
                        name = "front_grp",
                        transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.345, 0, 0.005, 1, },
                    },
                    { 
                        children = {
                            { 
                                materials = { "::/vehicle/bus/american_post_coach/mat/american_post_coach.mtl", },
                                mesh = "::/vehicle/bus/american_post_coach/msh/back_body_lod0.msh",
                                name = "back_body",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                            },
                            { 
                                animations = { 
                                    open_all_doors = { 
                                        params = { id = "::/vehicle/bus/american_post_coach/ani/open_all_doors/back_d1_lft.ani", },
                                        type = "FILE_REF",
                                    },
                                    -- close_all_doors, open_doors_left, close_doors_left
                                },
                                materials = { "::/vehicle/bus/american_post_coach/mat/american_post_coach.mtl", "::/vehicle/bus/american_post_coach/mat/american_post_coach_door.mtl", },
                                mesh = "::/vehicle/bus/american_post_coach/msh/back_d1_lft_lod0.msh",
                                name = "back_d1_lft",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.277, 0.787, -0.005, 1, },
                            },
                            { 
                                materials = { "::/vehicle/bus/emissive/bus_all_lights.mtl", },
                                mesh = "::/vehicle/bus/american_post_coach/msh/back_headlights_lod0.msh",
                                name = "back_headlights",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -0.005, 1, },
                            },
                            { 
                                children = {
                                    { 
                                        materials = { "/vehicle/bus/carnival_waggon_all/mat/carnival_waggon_generic.mtl", },
                                        mesh = "/vehicle/bus/carnival_waggon_all/msh/decoration_ballchain_01_lod0.msh",
                                        name = "decoration_ballchain_01",
                                        transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                                    },
                                },
                                name = "decoration_ballchain_inst_9",
                                transf = { 0.988, 0.147, 0.017, 0, -0.145, 0.985, -0.087, 0, -0.03, 0.083, 0.996, 0, 0.649, -1.041, 2.454, 1, },
                            },
                            { 
                                children = {
                                    { name = "harness_handle_attach", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.899, 0, 2.799, 1, }, },
                                },
                                name = "back_comp_grp",
                                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
                            },
                            -- back_w2 (Hinterachse), Glas, Innenraum, rechte Tür, weitere Dekorationen
                        },
                        name = "back_grp",
                        transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0.005, 1, },
                    },
                    { 
                        children = {
                            { children = { { name = "horse_grp_l", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, }, },
                              name = "left_horse", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.389, 0, 1, }, },
                            { children = { { name = "horse_grp_r", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, }, },
                              name = "right_horse", transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.389, 0, 1, }, },
                        },
                        name = "horse_grp",
                        transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3.579, 0, 0, 1, },
                    },
                },
                name = "RootNode",
                transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
            },
            visibleFrom = 0,
            visibleTo = 100,
        },
        -- LOD 1 (visibleFrom = 100, visibleTo = 400) und LOD 2 (400 bis 1000) mit den Meshes _lod1 und _lod2
    },
    metadata = { 
        availability = { yearFrom = 1900, yearTo = 1925, },
        cost = { price = -1, },
        description = { 
            description = _("VEHICLE_BUS_CARNIVAL_WAGGON_DESCRIPTION"),
            name = _("VEHICLE_BUS_CARNIVAL_WAGGON_NAME"),
        },
        draftAnimalProvider = { 
            draftAnimals = {
                { group = "horse_grp_l", model = "::/animal/draft_horse/draft_horse.mdl", transf = { ... }, transfOverride = { ... }, },
                { group = "horse_grp_r", model = "::/animal/draft_horse/draft_horse.mdl", transf = { ... }, transfOverride = { ... }, },
            },
        },
        emissions = { 
            noise = { score = 8.25, },
            pollution = { score = 4, },
        },
        extent = { bbMax = { 5.898, 1.09, 3.395, }, bbMin = { -2.22, -1.09, -0.002, }, },
        landVehicle = { 
            engines = {
                { power = 8, tractiveEffort = 3, type = "HORSE", },
            },
            topSpeed = 7.8,
            weight = 1,                       -- älteres Feld, in Tonnen; aktuelle Modelle schreiben weightEmpty in kg
        },
        lightSourceList = { 
            lights = {
                { 
                    angleMax = 30, angleMin = 1,
                    color = { 0.796, 0.663, 0.376, },
                    fromTime = 18, toTime = 7,
                    invertMask = true, lightId = "", mask = 0,
                    meshId = "back_headlights",
                    mode = "LIGHT", radius = 1.7, strength = 3,
                    transf = { 0.985, 0, -0.174, 0, 0, 1, 0, 0, 0.174, 0, 0.985, 0, 1.2, 0.8, 2.2, 1, },
                    type = "POINT",
                    visibleFrom = -1, visibleTo = -1,
                },
                -- zweite Lampe, Innenlicht
            },
        },
        maintenance = { lifespan = 21915, runningCosts = -1, },
        roadVehicle = { 
            config = { 
                axles = { "front_w1", "back_w2", },
                fakeBogies = {
                    {
                        { group = "front_grp", offset = 0, position = 3.58, },
                        { group = "back_grp", offset = 0, position = 1.35, },
                        { group = "horse_grp", offset = 0, position = 3.58, },
                    },
                    -- LOD 1 genauso, LOD 2 mit je zwei Fake-Drehgestellen an front_grp und back_grp
                },
                steeringParts = { },
                wheels = { },
            },
        },
        seatProvider = { 
            crewModels = { },
            drivingLicense = "BUS",
            seats = {
                { 
                    animation = "driving_upright",
                    crew = true,
                    group = "back_comp_grp",
                    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.49, -0.012, 1.999, 1, },
                },
            },
        },
        soundConfig = { 
            soundSet = { name = "::/vehicle/bus/shared/sound/bus_horse.snd", },
        },
        transformatorConfig = { 
            skipFromLod = 2,
            transformator = { name = "::/vehicle/shared/default_road.trf", },
        },
        transportVehicle = { 
            carrier = "ROAD",
            compartmentsList = {
                { 
                    loadConfigs = {
                        { 
                            cargoEntry = { 
                                capacity = 24,
                                cargoTypeSet = { 
                                    cargoClassesExcluded = { },
                                    cargoClassesIncluded = { "PASSENGERS", },
                                    cargoTypesExcluded = { },
                                    cargoTypesIncluded = { },
                                },
                                loadIndicator = "",
                                seats = { },
                            },
                            toHide = { },
                        },
                    },
                },
            },
            engineTags = { "BUS", },          -- ältere Namen; aktuelle Modelle schreiben engineTransportModes
            loadSpeed = 2,
            tags = { "BUS", },                -- und transportModes
        },
        versioning = { __version = "_v03", },
    },
    version = 2,
}
end

Die Kampagnendatei nutzt noch drei ältere Feldnamen, die Ladefunktionen in base/content/base/base/model_metadata_util.lua umwandeln: landVehicle.weight in Tonnen wird zu weightEmpty in kg (convertWeight), tags und engineTags werden zu transportModes und engineTransportModes (addTransportVehicleTransportModes), und compartmentsList wird zu compartments (toCompartmentList). In einer neuen Datei schreibst du die aktuellen Namen wie beim eCitaro: weightEmpty = 1000, ein weightMaxPayload, transportModes = { "BUS", } und engineTransportModes = { "BUS", }. Die beiden Lampen leuchten von 18 bis 7 Uhr (fromTime, toTime). Der Model Editor führt versioning.__version unter seinen Metadatenfeldern (base/content/model_editor/model_editor/meta_metadata.lua). Was das Spiel damit macht, ist nicht dokumentiert.

So machst du aus der Datei dein eigenes Fahrzeug:

  • Ersetze Meshes, Materialien und Animationen durch deine eigenen (msh/..., mat/..., ani/... relativ zum Modell) oder behalte Dateien des Basisspiels mit ::/. Eigene Dateien kannst du wie die Dekorationen auch über einen /-Pfad aus dem content/-Ordner deines Mods ansprechen.
  • Benenne die Knoten passend zu deinem Modell um und verwende dieselben Namen in roadVehicle.config, seats[].group, draftAnimals[].group, lightSourceList.lights[].meshId und transfOverride. Ein Bus ohne Pferde lässt draftAnimalProvider und den Knoten horse_grp weg und führt seine Vorderräder in wheels.
  • Gib dem Fahrzeug eigene Übersetzungsschlüssel in description und eigene Icons (siehe Icons).
  • Lass cost.price = -1 und maintenance.runningCosts = -1 stehen, damit das Spiel Preis und Betriebskosten berechnet (siehe Gemeinsame Blöcke).
  • Wähle Soundset und Transformator wie hier mit ::/ aus dem Basisspiel.

Repaints

Ein Repaint ergänzt ein vorhandenes Fahrzeug um eine neue Lackierung. Das Wiki nennt drei Stufen: neue Texturen, neue Texturen mit zusätzlichen Meshes und neue Texturen mit geänderten Meshes. Nur die letzte Stufe braucht Kopien der Originaldateien und damit die Erlaubnis des ursprünglichen Autors. Auch bei den ersten beiden erwartet die Community, dass du vorher den Autor fragst. Viele Autoren geben dann ihre Arbeitsdateien für die Texturen heraus.

Liefere nur neue oder geänderte Dateien aus, gib dem Repaint eine eigene modId und trage den Original-Mod in mod.json als Abhängigkeit ein (Abhängigkeiten). Ein Repaint eines Fahrzeugs aus dem Basisspiel braucht keine Abhängigkeit. Im Wiki gibt es ein Beispiel-Repaint des eCitaro aus dem Basisspiel zum Herunterladen.

Ein Repaint, das nur Texturen ändert, enthält:

  • die üblichen Mod-Dateien (mod.json, _metadata/modinfo.json, siehe Aufbau eines Mods); den ursprünglichen Autor als Co-Autor zu nennen, gehört zum guten Ton
  • eine .mtl für jedes Material, das neue Texturen bekommt
  • eine .mdl für jede neue Variante
  • die drei Icons jeder neuen Variante
  • die Texturen

Ein Repaint mit den Werten des Originals ändert nichts an der Simulation. Die offiziellen DLCs, die Fahrzeuge hinzufügen, setzen in ihrer mod.json "cosmetic": true und "severityRemove": "Warning". Das Wiki verlangt Warning, wenn nach dem Entfernen eines Mods Fahrzeugmodelle fehlen. Ob ein Fahrzeug mit neuen Werten noch als kosmetisch gilt, entscheidet der Modder nach der Regel des Wikis (nichts, was die Simulation beeinflusst), siehe mod.json.

Materialien

Kopiere die originale .mtl und ändere die Texturverweise. Ein Fahrzeugmaterial hat meist vier Maps, die ein Repaint ändern darf: map_albedo_opacity (Farbe und Transparenz), map_normal (Normal Map, z. B. für erhabene Kennzeichen), map_metal_gloss_ao (Metall, Glanz, Ambient Occlusion) und map_cblend_dirt_rust (wo der Spieler umfärben kann und wo Schmutz und Rost erscheinen). Aus base/content/vehicle/bus/ecitaro/ecitaro/mat/ecitaro.mtl (gekürzt):

map_albedo_opacity = { 
    fragmentSamplers = { 
        albedoOpacityTex = { 
            fileName = "tex/ecitaro_albedo_opacity.dds",
            type = "TWOD",
            wrapS = "REPEAT",
            wrapT = "REPEAT",
        },
    },
},
-- map_normal, map_metal_gloss_ao und map_cblend_dirt_rust sehen genauso aus
map_dirt = { 
    fragmentSamplers = { 
        dirtTex = { fileName = "/vehicle/shared/mat/tex/dirt_albedo.dds", ... },  -- in der Kopie: "::/vehicle/shared/mat/tex/dirt_albedo.dds"
    },
},

Die Texturpfade sind relativ zum Material. In der Kopie muss jede Textur, die du behältst, auf das Original zurückzeigen: Schreibe "::/vehicle/bus/ecitaro/mat/tex/ecitaro_normal.dds" für eine Datei des Basisspiels oder "<original_modid>::/..." für einen anderen Mod. Die relative Form "::tex/..." funktioniert nur, wenn deine Datei am selben Pfad liegt wie das Original. Auch die gemeinsamen Schmutz- und Rosttexturen (/vehicle/shared/mat/tex/dirt_albedo.dds, dirt_normal.dds, rust_albedo.dds, rust_normal.dds) brauchen das Präfix: Ohne es zeigt ein Pfad, der mit / beginnt, in deinen Mod. Die Regeln stehen unter Dateiverweise innerhalb von Inhalten.

Kopiere jedes Material, das eine Textur liest, die du änderst. Beim eCitaro lesen alle fünf Materialien tex/ecitaro_albedo_opacity.dds: ecitaro.mtl für die Karosserie, ecitaro_glass.mtl und ecitaro_int.mtl für Fenster und Innenraum sowie ecitaro_lod.mtl und ecitaro_int_lod.mtl, die die beiden niedrigeren LODs verwenden. Ein Material, das du auslässt, zeigt in seinem Teil des Modells oder ab einer gewissen Entfernung weiter die alte Lackierung.

Modell

Kopiere die originale .mdl und ändere drei Dinge: die Materialverweise in jedem LOD, availability und description (mit neuen Übersetzungsschlüsseln):

lods = {
    {
        node = {
            children = {
                {
                    materials = { "mat/ecitaro_red.mtl", },
                    mesh = "::/vehicle/bus/ecitaro/msh/body_lod0.msh",
                    -- ...
                },
                -- ...
metadata = {
    availability = { yearFrom = 2019, yearTo = 0, },
    description = {
        description = _("VEHICLE_BUS_ECITARO_RED_DESCRIPTION"),
        name = _("VEHICLE_BUS_ECITARO_RED_NAME"),
    },
    -- alles andere wie im Original
},

Alles, was du nicht kopierst, bleibt im Original. Jeder andere Verweis in der kopierten Datei braucht deshalb ein Präfix. Relative Pfade wie msh/body_lod0.msh, ani/open_doors_right/d1.ani und die Materialien, die du behältst, werden zu ::/vehicle/bus/ecitaro/msh/body_lod0.msh usw. Der eCitaro hat außerdem 22 Verweise, die mit / beginnen: die leuchtenden Lichtmaterialien (/vehicle/bus/emissive/bus_all_lights.mtl), die gemeinsamen Lichtanimationen (/vehicle/shared/ani/brake_lights_on.ani), das Soundset und den Transformator. Auch sie werden zu ::/vehicle/..., denn ohne Präfix zeigen sie in deinen Mod. Beim Repaint eines Fahrzeugs aus einem anderen Mod nimmst du <original_modid>::/... für dessen Dateien und ::/ für die Dateien des Basisspiels, die es verwendet.

Um das Repaint im Fahrzeugladen unter dem Original zu gruppieren, setzt du groupFileName in transportVehicle, siehe Gruppen im Fahrzeugladen. Die dort gezeigte relative Form erreicht nur Modelle im selben Mod. Für den eCitaro schreibst du den vollen Namen, groupFileName = "::/vehicle/bus/ecitaro/ecitaro.mdl". Das Leasing-Beispiel auf der Wiki-Seite zum Mod-Skript macht es genauso mit dem Namen aus dem Modell-Repository. Der Fahrzeugladen übernimmt den Wert unverändert (base/content/gui/gui/line_vehicle_mgmt/vehicle_store_util.tl) und gruppiert die Variante unter dem Modell, dessen Name im Repository diesem Wert entspricht. Kein ausgeliefertes Fahrzeug nutzt das Feld. Ob die Form mit ::/ zum Namen eines Modells aus dem Basisspiel passt, ist deshalb nicht bestätigt.

Icons

Das Wiki nennt drei Icons. Der Button SCREENSHOT im Model Editor (oben rechts) erzeugt sie im richtigen Ordner (siehe Icons und Screenshots). Die Dateinamen im Basisspiel weichen leicht von denen im Wiki ab: Der eCitaro hat icons/ecitaro_store.tga, icons/ecitaro_icon_small@2x.tga und icons/ecitaro_icon20@2x.tga, dazu _cblend-Masken für das kleine Icon und das 20-px-Icon. Das Wiki schreibt das mittlere als <vehiclename>_small_icon. Setzt description keine Icons, verwendet addIconMetadata diese Standardwerte, mit <vehicle> als Dateiname des Modells:

Feld in description Standard Verwendet für
icon3d icons/<vehicle>_store.tga Details im Fahrzeugladen
iconSmall, iconSmallCblend icons/<vehicle>_icon_small.tga, ..._icon_small_cblend.tga kompakte Liste im Fahrzeugladen
icon20, icon20cblend icons/<vehicle>_icon20.tga, ..._icon20_cblend.tga andere Fenster und Listen

Icons werden in doppelter Größe unter einem Namen mit @2x.tga gespeichert (siehe Texturen), die Standardwerte nennen die Datei aber ohne @2x. Die Icon-Pfade sind relativ zum Modell, ein Repaint legt seine Icons also in einen Ordner icons/ neben seiner .mdl.

Texturen

Die DDS-Kompression kostet Details, male deshalb nicht über die ausgelieferten .dds-Dateien. Frag nach Möglichkeit den Autor nach seinen Arbeitsdateien. Die allgemeinen Regeln für Texturen stehen unter Texturen.

Offizielles Wiki: Vehicle basics, Vehicle types, Repaint mods, Vehicle advanced topics, Cargo, Model definition (.mdl).