Güter, Namen, Personen und Sounds¶
Manche Inhaltstypen sind kleine Konfigurationsdateien ohne eigenes Modell und ohne eigene Konstruktion: Güterarten, Sprachen, Namenslisten, Figurenmodelle, Wiedergabelisten für Musik und Soundsets. Diese Seite zeigt zu jedem Typ eine Datei aus dem Basisspiel und verlinkt die offizielle Wiki-Seite dazu.
Die Pfade unten stammen aus dem entpackten Basisspiel. Dort wurde jedes Inhaltsarchiv zu einem gleichnamigen Ordner, deshalb kommen Ordnernamen doppelt vor, etwa base/content/cargos/iron_ore/iron_ore/iron_ore.cargo.lua. Das Spiel selbst spricht diese Datei als /cargos/iron_ore/iron_ore.cargo an (siehe Aufbau eines Mods). Ein Mod legt dieselben Dateien in seinen eigenen content/-Ordner.
| Inhalt | Dateityp | Ordner im Basisspiel | Wiki-Seite |
|---|---|---|---|
| Güterarten | .cargo.lua |
content/cargos/<cargo>/ |
Cargos |
| Güterklassen | .cargoclass.lua |
content/cargos/classes/ |
Cargos |
| Gütermodellformate | .cmf.lua |
content/cargos/formats/ |
Cargos |
| Sprachen | .lang.lua |
content/locale/ |
Localizations |
| Übersetzungen eines Mods | strings.json |
neben mod.json |
Mod definition |
| Namenssets | .names.lua |
content/names/<set>/ |
Names |
| Figuren | .mdl mit metadata.person |
content/characters/ |
People |
| Wiedergabelisten | .plist.lua |
content/music/<playlist>/ |
Playlists |
| Soundsets | .snd.lua |
sound/-Ordner neben den Modellen |
Sound sets |
Wie andere Ressourcendateien ist jede davon eine Lua-Datei mit einer Funktion data(), die eine Tabelle zurückgibt. Anzeigenamen stehen in _(), damit sie übersetzt werden können (siehe Übersetzungen).
Güter¶
Güter sind in drei Ebenen aufgebaut. Eine Güterart ist eine einzelne Ware, zum Beispiel Eisenerz. Güterklassen fassen Arten mit gemeinsamen Eigenschaften zusammen, etwa BULK. Gütermodellformate sind genormte Begrenzungsquader. Mit ihnen zeigt ein Fahrzeug oder Lager für jede Ladung das passende Modell.
Güterarten¶
Das Basisspiel hat 37 Güterarten, jede in einem eigenen Ordner unter base/content/cargos/. Aus base/content/cargos/iron_ore/iron_ore/iron_ore.cargo.lua:
function data()
return
{
name = _("Iron Ore"),
weightFactor = 1.0,
order = 210,
icon = "iron_ore.tga",
cargoClasses = { "UNIVERSAL", "BULK" },
categoryList = {
categories = {
"temperate.eco",
"dry.eco",
"tropic.eco",
"subarctic.eco"
}
},
scriptRef = {
fileName = "::/economy/cargotype.script@incomeFactors.constantFn",
params = {
initialPrice = 4,
finalPrice = 3.2,
decayRate = 0.005,
decayMod = 1,
decayStart = 200,
}
},
color = { 0.94, 0.53, 0.31},
loadSpeedFactor = 0.0625,
timeToDeliverInSeconds = 4500,
}
end
| Feld | Bedeutung (Wiki) |
|---|---|
name |
Anzeigename, übersetzbar. |
weightFactor |
Dichte. 1,0 für schwere Güter, weniger für leichte; Passagiere haben 1/6. |
order |
Position in Güterlisten. |
icon |
Gütersymbol. Symbole sind in doppelter Auflösung (@2x.tga) 50 px hoch, flach und schwarz umrandet. |
cargoClasses |
Klassen-Tags, zu denen die Art gehört. |
categoryList.categories |
Wirtschaften (.eco), in denen es die Art gibt, siehe Wirtschaften. |
scriptRef |
Einnahmefunktion und ihre params (siehe unten). |
color |
RGB-Farbe (0 bis 1) für Diagramme mit mehreren Güterarten. |
loadSpeedFactor |
Ladegeschwindigkeit pro Einheit. Passagiere haben 1, andere Güter 0,0625. |
timeToDeliverInSeconds |
Wie lange die Lieferung dauern darf, bevor die Kunden unzufrieden werden. |
Das Eisenerz-Beispiel im Wiki hat initialPrice = 3.5 und finalPrice = 2.9, die mitgelieferte Datei oben 4 und 3,2. Die Wiki-Zahlen sind also ein älterer Stand der Balance. Die Basisdateien verwenden außerdem zwei Felder, die das Wiki nicht beschreibt: category (ein kleingeschriebener Schlüssel wie "coal" oder "passengers", in 27 der 37 Arten) und id (nur in passengers.cargo.lua mit "PASSENGERS" und in rubber.cargo.lua mit "RUBBER"). Was sie bewirken, ist nicht dokumentiert. Die Wirtschaften und Klimazonen hinter categoryList beschreibt ModelMetadata.CategoryList, den Inhalt einer Wirtschaftsdatei die Seite Spielmechaniken.
Skripte finden geladene Güterarten in api.res.cargoTypeRep (siehe Repositorys). find erwartet den Ressourcennamen, also den Spielpfad ohne .lua, wie in api.res.cargoTypeRep.find("::/cargos/fish/fish.cargo") aus base/content/mission/mission/tutorial/tutorial_story_branch_b.tl. Für einen unbekannten Namen kommt eine negative ID zurück. getName macht aus einer ID wieder diesen Namen, und getPassengerCargoTypeId() liefert die ID der Passagiere.
Die Einnahmefunktion¶
scriptRef.fileName zeigt in der Form file@path.to.function auf eine Funktion. Laut Wiki ruft das Spiel sie mit der Tabelle params und mit timeInMs auf, der Zeit zwischen Abholung und Abgabe. Den zurückgegebenen Faktor multipliziert es mit der Luftlinie zwischen Abhol- und Abgabeort. Liegt der Abgabeort höher, kommt das Achtfache des Höhenunterschieds zur Strecke hinzu, und der Schwierigkeitsgrad skaliert das Ergebnis.
Alle Güterarten des Basisspiels außer den Passagieren verwenden incomeFactors.constantFn aus base/content/economy/economy/cargotype.script.lua. Diese Funktion beachtet die Zeit gar nicht:
constantFn = function(captureParams, timeInMs)
if captureParams.initialPrice and captureParams.finalPrice then
return captureParams.initialPrice + (captureParams.finalPrice - captureParams.initialPrice) * 0.25
else
return 6
end
end,
Für Eisenerz ergibt das 4 + (3,2 − 4) × 0,25 = 3,8 pro Entfernungseinheit, egal wie lange die Fahrt dauert. decayRate, decayMod und decayStart wirken nur in logisticDecayFn aus derselben Datei, und die nutzt keine Güterart des Basisspiels. Passagiere verwenden passengersFn, die ebenfalls einen festen Faktor liefert. In der Datei stehen noch stoneFn, constructionMaterialsFn und baseFactorFn. Soll eine Güterart mit der Zeit an Wert verlieren, lass scriptRef auf logisticDecayFn zeigen oder auf eine eigene Funktion mit denselben zwei Parametern.
Güterklassen¶
Die sechs Klassen liegen in base/content/cargos/classes/classes/. Aus bulk.cargoclass.lua:
function data()
return {
tag = "BULK",
name = _("Bulk"),
description = "",
icon = "bulk.tga",
order = 3,
color = { 254/255, 202/255, 23/255 },
}
end
tag ist der Schlüssel, mit dem andere Dateien die Klasse ansprechen (nur Großbuchstaben A-Z). color färbt die Klassen-Labels im „Fahrzeugladen“ (Vehicle Store) und im Baumenü. Laut Wiki werden description und icon bisher nirgends angezeigt. Die Basisdatei lässt description leer, wo das Wiki-Beispiel einen übersetzten Text hat.
| Klasse | Verwendung (Wiki) |
|---|---|
UNIVERSAL |
Jede normale Güterart. Arten dieser Klasse brauchen Modelle für die Formate BIG und SMALL. |
PASSENGERS |
Nur Passagiere. |
GOODS |
Stückgut, das in gedeckten Güterwagen fährt. |
FLATBED |
Große, schwere Ladung auf flachen Fahrzeugen. |
BULK |
Geschüttete Ladung wie Stein, Erz und Kohle in offenen Fahrzeugen mit hohen Bordwänden. Braucht die Bulk-Formate. |
LIQUID |
Flüssigkeiten in Kesselwagen und Tanklastwagen. Braucht die Formate CIRCLE_*. |
Mods dürfen Klassen hinzufügen. Urban Games bittet aber darum, vorher Kontakt aufzunehmen, damit die Mods untereinander kompatibel bleiben.
Gütermodellformate¶
Ein Format besteht aus einem tag und einer size in Metern, gemessen vom Wurzelknoten des Modells nach außen und nach oben. Aus base/content/cargos/formats/formats/rect_20x5.cmf.lua:
Das Wiki hat eine Tabelle der Standardformate und der Klassen, die sie brauchen. Der Ordner im Basisspiel enthält 28 Formate und weicht an zwei Stellen von dieser Tabelle ab:
- Das Wiki führt
MEDIUM2x1undMEDIUM4x1mit 11,0 × 21,0 × 5,0 (genau wieBUNKER_11x21). In den Basisdateien stehtsize = { 3.37, 2.2, .3 }fürmedium2x1.cmf.luaund{ 8.34, 2.8, .3 }fürmedium4x1.cmf.lua. - Fünf Formate des Basisspiels fehlen im Wiki:
PILE_15x15,PILE_30x30,PILE_50x20,SQUARE_1_2undSQUARE_1_7. Eisenerz bringt Modelle fürSQUARE_1_2undSQUARE_1_7mit, Schüttgüter verwenden sie also.
Welche Formate ein Schüttgut in der Praxis braucht, zeigt ein Ordner aus dem Basisspiel: In base/content/cargos/iron_ore/iron_ore/ liegen iron_ore_big.mdl, iron_ore_small.mdl, iron_ore_bunker_11x21.mdl, elf Dateien iron_ore_rect_*.mdl und die beiden square-Modelle.
Güterart-Sets¶
Fahrzeuge, Stationen und Gütermodelle wählen ihre Güter mit einem CargoTypeSet. Das Wiki nennt die Reihenfolge, in der die vier Listen wirken: Die Menge beginnt leer, dann kommen alle Arten aus cargoClassesIncluded hinzu, die aus cargoClassesExcluded fallen weg, cargoTypesIncluded kommt hinzu und cargoTypesExcluded fällt weg. Klassen stehen als Tag in der Liste, Arten als Dateiname ohne .lua:
cargoTypeSet = {
cargoClassesIncluded = { "PASSENGERS", },
cargoClassesExcluded = { },
cargoTypesIncluded = { },
cargoTypesExcluded = { },
},
Gütermodelle¶
Ein Gütermodell ist ein gewöhnliches Asset-Modell mit einem Block cargoModel in den Metadaten (siehe Modelle und Assets). Aus base/content/cargos/iron_ore/iron_ore/iron_ore_big.mdl:
cargoModel = {
cargoTypeSet = {
cargoClassesExcluded = { },
cargoClassesIncluded = { },
cargoTypesExcluded = { },
cargoTypesIncluded = { "iron_ore.cargo", },
},
formats = { "BIG", },
},
formats nennt die Formate, die das Modell ausfüllt. Ein optionales availability mit yearFrom und yearTo (ModelMetadata.Availability) begrenzt das Modell auf einen Zeitraum. Ladeanzeigen, die zufällige Gütermodelle auswählen, halten sich daran.
Übersetzungen¶
Übersetzter Text besteht aus zwei Teilen: den Schlüsseln in den Dateien und den Tabellen oder Katalogen, die aus Schlüsseln Text machen.
Schlüssel in Skriptdateien¶
Jeder Text, den der Spieler sieht, steht in _(): name = _("Iron Ore"). Das Argument ist entweder englischer Text (_("Iron Ore"), _("English")) oder ein Schlüssel (_("MISSION_08_NAME") in mods/release/urbangames_campaign_mission_08/content/info.mission.lua). Gibt es keine Übersetzung, zeigt das Spiel das Argument selbst an. Englischer Text als Schlüssel braucht deshalb keinen englischen Eintrag.
Für Pluralformen und Kontext gibt es nGetText, pGetText und npGetText. Kontext-Strings trennt <|ctx|>, wie in "map-size<|ctx|>Small" in base/mod.json. Zahlen und Prozentwerte formatieren LangUtil und api.util.lang passend zur eingestellten Sprache.
Übersetzungen in einem Mod: strings.json¶
Ein Mod übersetzt seine Schlüssel in einer strings.json neben der mod.json, mit einem Objekt pro Sprachcode. Das Beispiel aus dem Wiki:
{
"en" : {
"brake_intensity_name" : "Brake Intensity",
"brake_intensity_tooltip" : "Adjust the global brake intensity."
},
"de" : {
"brake_intensity_name" : "Bremsintensität",
"brake_intensity_tooltip" : "Passen Sie die globale Bremsintensität an."
}
}
Die Schlüssel im Wiki-Beispiel sind Name und Tooltip eines Parameters. Die strings.json ist also für name, tooltip und values deiner Mod-Parameter (params in mod.json) gedacht und ebenso für die _("...")-Schlüssel in deinen Modellen und Skripten. Kein mitgelieferter Mod hat eine strings.json, mit der sich das bestätigen ließe. Fehlt ein Schlüssel in der eingestellten Sprache, nimmt das Spiel den englischen Text, und fehlt auch der, zeigt es den Schlüssel. Weil keiner der offiziellen Mods in mods/release/ oder dlcs/ eine strings.json mitbringt, stehen ihre Texte MISSION_* und M8_* nicht in den Spieldateien, aus denen diese Dokumentation entsteht.
Zum Sprachcode: Laut Wiki legst du ein Objekt pro locale-Kennung an, also <language>_<region> wie de_CH, und das einfache de greift für alle deutschen Varianten ohne eigenes Objekt. Sein strings.json-Beispiel verwendet die Kurzcodes en und de, die mitgelieferten .lang.lua-Dateien dagegen regionale Codes wie de_DE und en_US (Tabelle unten). Für einen Mod sind Kurzcodes die sichere Wahl. Ein regionales Objekt brauchst du nur für Text, der abweicht, etwa für Schweizerdeutsch.
Sprachdateien¶
Eine Sprache selbst ist eine .lang.lua-Datei. Aus base/content/locale/locale/de.lang.lua:
function data()
return {
name = "Deutsch",
locale = "de_DE",
fontMap = {
regular = "Lato2OFL/Lato-Regular.ttf",
bold = "Lato2OFL/Lato-Bold.ttf",
light = "Lato2OFL/Lato-Light.ttf",
medium = "Lato2OFL/Lato-Medium.ttf",
monoRegular = "Noto/NotoSansMono-Regular.ttf",
monoBold = "Noto/NotoSansMono-Bold.ttf",
},
unicodeFontMap = {
regular = "Noto/NotoSansCJKsc-Regular.otf",
bold = "Noto/NotoSansCJKsc-Bold.otf",
monoRegular = "Noto/NotoSansMonoCJKsc-Regular.otf",
monoBold = "Noto/NotoSansMonoCJKsc-Bold.otf",
scaling = 0.9,
},
}
end
name ist der Eintrag in der Einstellung „Sprache“ (Language) unter „Einstellungen“ > „Oberfläche“. locale ist der Code, mit dem Mods ihre Übersetzungen abgleichen und der das Zahlenformat bestimmt. Die beiden Font-Tabellen wählen die Schriften aus, die in base/content/locale/locale/Lato2OFL/ und Noto/ liegen.
| Datei | name |
locale |
|---|---|---|
de.lang.lua |
Deutsch | de_DE |
en.lang.lua |
English | en_US |
es.lang.lua |
Español | es_ES |
fr.lang.lua |
Français | fr_FR |
it.lang.lua |
Italiano | it |
ja.lang.lua |
日本語 | ja_JP |
ko.lang.lua |
한국어 | ko |
nl.lang.lua |
Nederlands | nl_NL |
pl.lang.lua |
Polski | pl_PL |
pt_BR.lang.lua |
Português | pt_BR |
ru.lang.lua |
Pусский | ru_RU |
zh_CN.lang.lua |
简体中文 | zh_CN |
zh_TW.lang.lua |
繁體中文 | zh_TW |
Die .lang.lua-Datei enthält keinen Text. Laut Wiki liegen die Texte einer Sprache als kompilierte gettext-Kataloge in strings/<locale>/LC_MESSAGES/base.mo. Diese Dateien gehören nicht zu den hier verwendeten Quellen des Basisspiels. Für eine Übersetzung des Spiels in eine neue Sprache beschreibt das Wiki den üblichen gettext-Ablauf:
- Eine vorhandene
base.moin eine bearbeitbare.po-Datei umwandeln, mitmsgunfmt(msgunfmt "xyz.mo" -o "xyz.po") oder der Python-Bibliothek polib. - Die
.poin einem Texteditor oder in Poedit übersetzen, nachdem du unter Catalogue > Properties die Zielsprache eingestellt hast. Platzhalter wie%1%oder{townName}bleiben stehen, und jede Pluralform muss ausgefüllt sein. - Mit File > Compile to MO in Poedit wieder zu
.mokompilieren. (Das Wiki nennt auch für diesen Schrittmsgunfmt, das gettext-Werkzeug zum Kompilieren heißt abermsgfmt.) - Nach einem Spiel-Update neue Schlüssel mit Catalog > Update from POT file übernehmen.
Die Anleitung Missionen beschreibt Keyframe-Dateien pro Sprache für Untertitel und Sprachausgabe.
Namen¶
Ein Namensset liefert Personen-, Stadt- und Straßennamen, und der Spieler wählt eines unter „Namen“ (Names) in den Einstellungen für ein neues Spiel aus. Das Basisspiel hat 18 Sets in base/content/names/names/. Aus england/england.names.lua:
function data()
return
{
name = _("English"),
personNamesScript = {
fileName = "/names/names.script@personNameScriptFn",
params = {
path = "unitedKingdom"
}
},
townNamesScript = {
fileName = "/names/names.script@townsNameScriptFn",
params = {
languages = {
en = "en",
fallback = "en",
},
path = "england"
}
},
streetNamesScript = {
fileName = "/names/names.script@streetsNameScriptFn",
params = {
languages = {
en = "en",
fallback = "en",
},
path = "england"
}
},
}
end
Jeder Eintrag ...Script zeigt auf eine Funktion. Das Spiel ruft sie mit den hier angegebenen params auf (das Wiki nennt sie captureParams) und mit einer zweiten Tabelle:
| Funktion | Zweite Tabelle | Rückgabewert |
|---|---|---|
| Personennamen | lang, isMale |
ein String, z. B. "Madeline Lyla" |
| Stadtnamen | lang, num |
eine Liste mit num Namen; alle Namen bei num = -1 |
| Straßennamen | lang, num |
eine Liste mit num Namen; alle Namen bei num = -1 |
Das Wiki verlangt, dass Funktionen für Stadtnamen keine Dubletten liefern und genug Namen für eine volle Karte haben. Im Beispielcode des Wikis heißen die Funktionen townsNameScriptFn und streetNameScriptFn, im Basisskript townsNameScriptFn und streetsNameScriptFn (mit s).
So arbeiten die gemeinsamen Funktionen in base/content/names/names/names.script.lua:
personNameScriptFnnimmt Vor- und Nachnamen auspersonnameutil.luaim selben Ordner, ausnames[path][lang]odernames[path].en. Sie wählt einen männlichen oder weiblichen Vornamen und einen Nachnamen. MitlastNameFirst = truewie inchina.names.luasteht der Nachname vorn. Stattpathkann ein Set eine Listepathsmit einer passenden ListelastNameFirstangeben, dann wird pro Person einer der Einträge gewählt.townsNameScriptFnundstreetsNameScriptFnladen<modId>::/names/<path>/<language>/towns.luabzw.streets.lua. Die Sprache kommt aus der Tabellelanguages: das aktuellelang, wenn es dafür einen Eintrag gibt, sonstfallback. Jede dieser Dateien gibt eine einfache Liste von Strings zurück.england/en/towns.luabeginnt mit"Aberdeen", "Belfast", "Birmingham".
Die Tabelle languages in china.names.lua hat die Schlüssel de, en, zh_CN, zh_TW und fallback. lang kann also ein regionaler Code sein, das Wiki erwähnt nur Kurzcodes wie en.
Damit kommt ein Mod einfach zu neuen Stadt- und Straßennamen: Er verwendet das Basisskript, übergibt seine Mod-ID und bringt die Listen mit. Das Basis-Set oben darf /names/... schreiben, weil es zum Basisspiel gehört. Ein Mod verweist mit ::/names/... auf das Basisskript.
-- content/names/mynames/mynames.names.lua im Mod "author_mynames"
function data()
return {
name = _("My names"),
personNamesScript = {
fileName = "::/names/names.script@personNameScriptFn",
params = { path = "unitedKingdom" },
},
townNamesScript = {
fileName = "::/names/names.script@townsNameScriptFn",
params = { modId = "author_mynames", path = "mynames", languages = { fallback = "en" } },
},
streetNamesScript = {
fileName = "::/names/names.script@streetsNameScriptFn",
params = { modId = "author_mynames", path = "mynames", languages = { fallback = "en" } },
},
}
end
Die Listen kommen dann nach content/names/mynames/en/towns.lua und streets.lua. Das Basisskript liest den Parameter modId, aber keines der Basis-Sets nutzt ihn. Genau dieser Aufbau ist deshalb nicht erprobt. Personennamen lassen sich so nicht erweitern, weil die Personenfunktion nur die Tabellen in der personnameutil.lua des Basisspiels liest. Für neue Personennamen brauchst du eine eigene Funktion.
Figuren¶
Fußgänger, Passagiere und Fahrzeugbesatzungen sind Figurenmodelle: normale .mdl-Dateien mit einem Block person in den Metadaten. Das Basisspiel hat 37 davon in base/content/characters/characters/, zum Beispiel era_a_wom_01 oder era_b_driver_rail. Aus era_a_driver_water/era_a_driver_water.mdl (gekürzt):
metadata = {
availability = {
yearFrom = 1850,
yearTo = 1935,
},
colorConfig = {
configs = {
{
{ 0.360784, 0.357884, 0.352295, },
{ 0.266667, 0.266667, 0.266667, },
{ 0.596078, 0.596078, 0.596078, },
{ 0.623529, 0.554269, 0.542837, },
},
-- weitere Farbsets
},
},
person = {
drivingLicenses = { "WATER", },
gender = "MALE",
},
soundConfig = {
effects = {
select = {
"::/characters/shared/sound/selected_male_0.wav",
-- vier weitere
}
}
},
},
availabilitybegrenzt die Figur auf eine Epoche.yearFromundyearTokönnen fehlen oder 0 sein, dann gilt keine Grenze.colorConfig.configsist eine Liste von Farbsets zum Umfärben. Jedes Set hat vier RGB-Farben, eine pro Kanal der Color-Blend-Map des Materials.personkennzeichnet das Modell als Figur. IstdrivingLicensesleer, ist sie Fußgänger oder Passagier. Mit Führerscheinen gehört sie zur Besatzung der entsprechenden Fahrzeuge.genderist"FEMALE"oder"MALE"(Gender) und ordnet der Figur weibliche oder männliche Einwohnernamen zu.
Das Wiki nennt die Führerscheine BUS, TRUCK, TRAM, RAIL, WATER, AIR und AIR_OUTDOOR. Das Basisspiel verwendet zusätzlich HELICOPTER, in era_c_driver_helicopter.mdl. Welchen Führerschein die Besatzung eines Fahrzeugs braucht, steht in dessen seatProvider.drivingLicense (siehe Sitze). Von den 37 Figuren des Basisspiels haben 24 keinen Führerschein. Fahrer von Straßenfahrzeugen haben BUS, TRUCK und TRAM zusammen. Die Typdefinition ModelMetadata.Person deklariert nur gender, nicht drivingLicenses.
Die Liste soundConfig.effects.select fehlt im Wiki. Sie enthält die Sprachclips, die zu hören sind, wenn der Spieler die Figur auswählt.
Figurenmodelle brauchen diese Animationen, pro Knochen angegeben als animations = { <name> = { type = "FILE_REF", params = { id = "/characters/shared/ani/..." } } }:
| Animation | Verwendung (Wiki) |
|---|---|
driving |
Sitzen auf einem niedrigen Fahrersitz wie im Auto. |
driving_upright |
Sitzen auf einem höheren Fahrersitz wie in den meisten anderen Fahrzeugen. |
idle |
Stehen, z. B. beim Warten an Ampeln oder Stationen oder im Fahrzeug. |
sitting |
Sitzen, meist als Passagier. |
walk |
Gehen. |
Alle fünf laufen in Schleife. Die Figuren des Basisspiels teilen sich ihre Animationsdateien unter /characters/shared/ani/.
Wiedergabelisten für Musik¶
Eine Wiedergabeliste ist eine Liste von Musikstücken, die der Spieler unter „Standard-Wiedergabeliste“ (Default Playlist) in den „Audio-Einstellungen“ auswählen kann. Aus base/content/music/music/default/default.plist.lua (gekürzt):
function data()
return {
name = _("Transport Fever 3"),
tracks = {
{
name = "J.J. Ipsen - A Promise Made Is a Promise Kept",
fileName = "a_promise_made_is_a_promise_kept.ogg",
},
{
name = "J.J. Ipsen - Cup of Joe",
fileName = "cup_of_joe.ogg",
},
-- weitere Stücke
},
order = 1,
}
end
name ist der Name der Wiedergabeliste in der Liste „Standard-Wiedergabeliste“, order ihre Position dort. Jedes Stück hat einen fileName relativ zur .plist.lua-Datei (die .ogg-Dateien liegen im selben Ordner) und einen name für den „Musikplayer“ (Music Player). Das Deluxe Upgrade Pack bringt in dlcs/urbangames_deluxe_upgrade_pack/content/music/music/ vier weitere Wiedergabelisten mit. Sie haben order-Werte wie 300 und Stücke, die nur einen fileName haben, name darf also fehlen.
base/content/music/music/menu.plist.lua ist die Musik des Hauptmenüs und verwendet Felder, die das Wiki nicht behandelt: hidden = true hält sie aus der Auswahl „Standard-Wiedergabeliste“ heraus, die Stücke haben enableRepeat und instantTransition, fileName ist ein absoluter Pfad wie "::/music/tf3_theme_a_loop.ogg", und variants tauscht die Datei in einem bestimmten context aus (hier "Credits").
Soundsets¶
Ein Soundset (.snd.lua) fasst die Sounds eines Objekts zusammen: eines Fahrzeugs, einer Industrie, Station, eines Depots oder Stadtgebäudes. Das Basisspiel hat 100 davon. Ein Modell oder eine Konstruktion gibt das Soundset ohne .lua in soundConfig.soundSet.name an (ModelMetadata.SoundConfig). Aus base/content/vehicle/train/br75_4/br75_4/br75_4.mdl:
base/content/industries/coal_mine/coal_mine/coal_mine.con.lua verwendet einen relativen Namen, "sound/coal_mine.snd".
Aufbau¶
Ein Soundset gibt tracks, events und ein updateScript zurück. Tracks laufen in Schleife, Events spielen bei jedem Auslösen einmal. In jedem Frame setzt das Update-Skript Lautstärke (Gain) und Tonhöhe (Pitch) pro Track und entscheidet, welche Events spielen.
Im Wiki steht refDist (die Entfernung, auf die die Lautstärke des Sounds normiert ist) direkt an jedem Track und Event. Die mitgelieferte soundsetutil.lua schreibt es stattdessen in eine Tabelle attrs: { name = name, attrs = { refDist = refDist } } für einen Track und { names = names, attrs = { refDist = refDist } } für ein Event. Wenn du soundsetutil verwendest, musst du dich nicht entscheiden.
Das Update-Skript bekommt die Parameter aus der .snd.lua, eine Tabelle params mit dem aktuellen Zustand (params.currentInfo) und ein Ausgabeobjekt, einen SoundTransfOutput. Das Wiki nennt dessen zwei Methoden addTrack (einmal pro Track, in der Reihenfolge der Tracks) und addEvent. Die Typdefinition in base/tealdef/scripts/soundset.d.tl hat eine dritte, triggerEvent, und das soundset_default.script.tl des Basisspiels ruft triggerEvent für eigene Events auf und addEvent für die übrigen.
soundsetutil¶
Alle Soundsets des Basisspiels entstehen mit /scripts/soundsetutil.lua, das das Standard-Update-Skript einträgt. Aus base/content/industries/coal_mine/coal_mine/sound/coal_mine.snd.lua:
local soundsetutil = require "/scripts/soundsetutil.lua"
-- in einem Mod: require "::/scripts/soundsetutil.lua"
function data()
local data = soundsetutil.makeSoundSet()
soundsetutil.addSimpleTrackParam01(data, "coal_mine_producing.wav", 50.0, {"industry", "production01"})
soundsetutil.addSimpleTrackParam01(data, "coal_mine_boosted.wav", 60.0, {"industry", "boosted01"})
soundsetutil.addSimpleTrackParam01(data, "coal_mine_double_boosted.wav", 70.0, {"industry", "doubleBoosted01"})
return data
end
Die Namen der Sounddateien sind relativ zur .snd.lua-Datei. Rufe zuerst makeSoundSet() auf, danach die anderen Funktionen:
| Funktion | Fügt hinzu |
|---|---|
addEvent(data, key, names, refDist) |
Ein Event, das beim Auslösen immer spielt; bei mehreren Dateien wird eine zufällig gewählt. |
addEventParam01(data, key, names, refDist, gainCurve, pitchCurve, infoGain, infoPitch) |
Ein Event, dessen Gain und Pitch Kurven folgen. |
addEventCustom(..., customUpdateScript) |
Ein Event mit eigener Update-Funktion. |
addTrackDefault(data, name, refDist) |
Einen Track mit festem Gain und Pitch (in der Datei, nicht im Wiki). |
addTrackParam01(data, name, refDist, gainCurve, pitchCurve, infoGain, infoPitch) |
Einen Track, dessen Gain und Pitch Kurven folgen. |
addSimpleTrackParam01(data, name, refDist, infoGain) |
Einen Track mit Pitch 1, dessen Gain der Eingabewert ist. |
addTrackSqueal(data, name, refDist) |
Radquietschen aus Geschwindigkeit und Querkraft, z. B. über Weichen. |
addTrackBrake(data, name, refDist, maxGain) |
Bremsgeräusch aus Geschwindigkeit und Bremsung; moderne Bremsen bekommen ein niedrigeres maxGain. |
addEventClacks(data, names, refDist, axleRefWeight) |
Schienenstoß-Klacken aus Geschwindigkeit, Gewicht und Achszahl. |
addTrackCustom(..., customUpdateScript) |
Einen Track mit eigener Update-Funktion. |
makeRoadVehicle2(data, speeds, idle, idleSpeed, idleGain0, drive, driveSpeed, refDist, infoGain, infoPitch) |
Leerlauf- und Fahrgeräusche eines Straßenfahrzeugs. |
makeSteamTrain(data, idle, fast, tracksRefDist, chuffNames, chuffsRefDist, chuffsFastFreq, refWeight) |
Leerlauf- und Fahrgeräusche sowie Auspuffschläge einer Dampflok. |
Eine Kurve ist eine Liste von Punkten { input, value } (SampleCurveParams). Zwischenwerte werden interpoliert, Werte außerhalb auf den Rand begrenzt. Die Eingabe ist ein Paar von Schlüsseln in currentInfo. Diese Paare nutzen die Soundsets des Basisspiels am häufigsten:
| Eingabe | Verwendungen |
|---|---|
{"vehicle", "speed01"} |
81 |
{"industry", "production01"} |
34 |
{"industry", "boosted01"} |
34 |
{"vehicle", "power01"} |
32 |
{"industry", "idle01"} |
19 |
{"industry", "doubleBoosted01"} |
15 |
{"station", "crowd01"} |
12 |
{"station", "cargo01"} |
10 |
Eine eigene Update-Funktion bekommt ein result mit gain und pitch (beide beginnen bei 1), deine Kurvenparameter und currentInfo, bei Events zusätzlich die Info des vorigen Frames. Sie setzt result.trigger = true, um das Event abzuspielen (CustomUpdateScript, CustomUpdateEventScript). Setze trigger nur für einen Frame, sonst wiederholt sich der Sound. base/content/vehicle/bus/shared/shared/sound/bus_electric.snd.lua verwendet addTrackCustom mit "::/vehicle/shared/sound/accelerationOnlyTrackUpdate.script@updateFn". Sein Leerlaufbrummen ist deshalb nur zu hören, während der Bus beschleunigt.
Event-Schlüssel¶
Das Spiel löst diese Event-Schlüssel aus (laut Wiki):
| Schlüssel | Objekte | Wann |
|---|---|---|
random1 ... random64 (Zweierpotenzen) |
Stadtgebäude, Stationen, Industrien | etwa alle n Sekunden |
openDoors |
Straßen-, Schienen-, Wasser- und Luftfahrzeuge | nach der Ankunft an einer Station |
closeDoors |
Straßen-, Schienen-, Wasser- und Luftfahrzeuge | vor der Abfahrt von einer Station |
horn |
Schienen- und Wasserfahrzeuge | bei der Abfahrt von einer Station |
clacks |
Schienenfahrzeuge | regelmäßig während der Fahrt (Achsklacken) |
chuffs |
Schienenfahrzeuge | regelmäßig während der Fahrt (Auspuffschläge der Dampflok) |
sonicBoom |
Luftfahrzeuge | beim Durchbrechen der Schallmauer |
land |
Luftfahrzeuge | wenn die Räder den Boden berühren |
Die Soundsets des Basisspiels definieren horn für mehr Fahrzeuge, als das Wiki aufzählt: Busse, Autos, Lastwagen, Trams, Flugzeuge und Hubschrauber haben auch eines (zum Beispiel bus_electric.snd.lua oben). Die Feuerwerk-Soundsets in base/content/game_mechanics/game_mechanics/fun_elements/firework/ verwenden launch und bang, gesteuert über {"state", "launch"} und {"state", "explode"}. Laut Wiki lassen sich andere eigene Events nur von Kantenobjekten auslösen.
Bau-Audiosets¶
Ein Bau-Audioset (.builder_audio.lua) enthält die Sounds, die das Baumenü beim Bauen, Ziehen oder Abreißen spielt. Es ist kein Soundset: Es hat keine Tracks und kein Update-Skript, nur Listen von .wav-Dateien pro Aktion. Alle 29 Dateien des Basisspiels liegen in base/content/gui/gui/construction/sound/, die Clips in Unterordnern dort (build/, bulldoze/, terraform/, tool/), mit Pfaden relativ zur Datei. Aus buildoze_tree.builder_audio.lua (die Basisdateien schreiben tatsächlich „buildoze“):
function data()
return {
noMoney = {
"build/no_money.wav",
},
build = {
"build/asset.wav",
},
bulldoze = {
"bulldoze/tree.wav",
},
terraform = {
"terraform/brush_trees.wav",
},
}
end
Die Tabelle ist ein BuilderAudioSet. Die 29 Dateien verwenden die folgenden Schlüssel. Wofür jeder gedacht ist, ergibt sich aus dem Schlüsselnamen und den Dateien, die ihn setzen. Die Spieldateien beschreiben es nicht.
| Schlüssel | Wert | Dateien | Verwendet bei |
|---|---|---|---|
build |
Liste von Clips | 23 | Platzieren einer Konstruktion, eines Moduls, Assets, einer Straße, eines Gleises oder einer Werkzeugaktion |
bulldoze |
Liste von Clips | 24 | Abreißen; die Werkzeug-Sets verwenden tool/cancel.wav |
noMoney |
Liste von Clips | 20 | Bauen ohne genug Geld |
terraform |
Liste von Clips | 9 | Geländepinsel sowie Baum-, Fels- und Pflanzenpinsel |
dragStart, dragEnd |
ein Clip | 4 | Anfang und Ende beim Ziehen einer Straße, eines Gleises oder einer Spur |
drag |
Liste von Clips | 4 | während des Ziehens |
dragBridge, dragTunnel |
Liste von Clips | 3 | beim Ziehen über eine Brücke oder durch einen Tunnel |
dragOffset |
Zahl | 4 | in allen vier -0.2; die Wirkung ist nicht dokumentiert |
Die vier Sets mit Zieh-Schlüsseln sind buildoze_street, buildoze_track, tool_lane_modifier und fallback. Der Typ hat außerdem ein Feld attrs (SoundAttributes), das keine Basisdatei setzt.
Inhalte verweisen mit builderAudioRes und dem Spielpfad ohne .lua auf ein Set: .con.lua-, .module.lua-, .street_template.lua- und .edge.lua-Dateien auf oberster Ebene, Modelle in metadata.soundConfig.builderAudioRes (ModelMetadata.SoundConfig, genutzt von Bäumen, Felsen und Pflanzen) und die Werkzeuge des Baumenüs in ihrer Definition. Die Module verwenden "::/gui/construction/sound/buildoze_module.builder_audio". In dieser Form schreibt auch ein Mod den Verweis auf ein Basis-Set. Ein Mod kann ein eigenes Set unter content/ mitbringen und genauso darauf verweisen. Geladene Sets stehen in api.res.builderAudioSetRep. Der Schlüssel für Ressourcen-Modifier ist loadBuilderAudioSet, die Kategorie für Dateifilter builderAudioSet (siehe Ressourcen-Modifier).
Offizielles Wiki: Cargos, Localizations, Mod definition (strings.json), Names, People, Playlists, Sound sets.