From 8b97873f57e5733a04efb38c8d076d987fdcded2 Mon Sep 17 00:00:00 2001 From: Stefan Harmuth Date: Fri, 28 Aug 2026 13:48:51 +0200 Subject: [PATCH] cargo, partly --- constants.py | 39 +++++++++ loader.py | 236 ++++++++++++++++++++++++++++++++------------------- output.py | 128 +++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 89 deletions(-) diff --git a/constants.py b/constants.py index 779e682..238d623 100644 --- a/constants.py +++ b/constants.py @@ -1,3 +1,11 @@ +from enum import Enum + +LANGUAGES = { + "english": ("en", r"[strings]\strings_english.str"), + "korean": ("kr", r"[strings]\strings_korean.str"), +} +DEFAULT_LANGUAGE = "english" + COUNTRYS = {} DLC_NAMES = { @@ -62,3 +70,34 @@ DLC_OVERRIDES = { "UI_VEHICLE_ZIKZ_612H_MASTODONT_NAME": "dlc_mastodon", "UI_VEHICLE_KENWORTH_W990_NAME": "dlc_w990", } + + +class TruckInfo(str, Enum): + ALWAYS = "UI_TRUCK_INFO_ALWAYS" + CAPABLE = "UI_TRUCK_INFO_CAPABLE" + NOT_AVAILABLE = "UI_TRUCK_INFO_NA" + SWITCHABLE = "UI_TRUCK_INFO_ON_OFF" + + +class TruckAddonCategory(str, Enum): + ALL_WHEEL_DRIVE = "awd" + BUBBLE_HEAD = "bubble_head" + BUMPER = "bumper" + CURTAIN = "curtain" + DIFF_LOCK = "diff_lock" + EXHAUST = "exhaust" + FRAME_ADDON = "frame_addons" + FENDERS = "fenders" + FENDER_FRONT = "fender_front" + FENDER_REAR = "fender_rear" + GRILL = "grill" + HOOD_ORNAMENT = "hood_ornament" + MIRROR_MOUNT = "mirror_mount" + MISCELLANEOUS = "miscellenious" + REAR = "rear" + SNORKEL = "snorkel" + STICKER = "sticker" + STICKER_EXTERIOR = "sticker_exterior" + STICKER_WINDSHIELD = "sticker_windshield" + TOP = "top" + WHEEL_ADDON = "wheel_addon" diff --git a/loader.py b/loader.py index 1431a69..8012ced 100644 --- a/loader.py +++ b/loader.py @@ -1,5 +1,5 @@ from __future__ import annotations -from constants import DLC_OVERRIDES +from constants import * from decimal import Decimal, ROUND_HALF_UP from enum import Enum from html import parser as html_parser @@ -10,13 +10,6 @@ import os import re import sys -LOAD_LIST = "pak.load_list" -LANGUAGES = { - "english": ("en", r"[strings]\strings_english.str"), - "korean": ("kr", r"[strings]\strings_korean.str"), -} -DEFAULT_LANGUAGE = "english" - logger = logging.getLogger(__name__) html_parser.starttagopen = re.compile("<[a-zA-Z_]") html_parser.endtagopen = re.compile("") -class TruckInfo(str, Enum): - ALWAYS = "UI_TRUCK_INFO_ALWAYS" - CAPABLE = "UI_TRUCK_INFO_CAPABLE" - NOT_AVAILABLE = "UI_TRUCK_INFO_NA" - SWITCHABLE = "UI_TRUCK_INFO_ON_OFF" - - -class TruckAddonCategory(str, Enum): - ALL_WHEEL_DRIVE = "awd" - BUBBLE_HEAD = "bubble_head" - BUMPER = "bumper" - CURTAIN = "curtain" - DIFF_LOCK = "diff_lock" - EXHAUST = "exhaust" - FRAME_ADDON = "frame_addons" - FENDERS = "fenders" - FENDER_FRONT = "fender_front" - FENDER_REAR = "fender_rear" - GRILL = "grill" - HOOD_ORNAMENT = "hood_ornament" - MIRROR_MOUNT = "mirror_mount" - MISCELLANEOUS = "miscellenious" - REAR = "rear" - SNORKEL = "snorkel" - STICKER = "sticker" - STICKER_EXTERIOR = "sticker_exterior" - STICKER_WINDSHIELD = "sticker_windshield" - TOP = "top" - WHEEL_ADDON = "wheel_addon" - - def get_initial_pak_path(): return os.path.join( str(os.path.expanduser("~")), @@ -219,10 +181,10 @@ class SnowrunnerClass: self.load_desc(data.children["uidesc"]) def load_data(self, data: SnowRunnerXNLNode) -> None: - raise NotImplementedError() + raise NotImplementedError(f"load_data() in {self.__class__.__name__}()") def update_data(self, data: SnowRunnerXNLNode) -> None: - raise NotImplementedError() + raise NotImplementedError(f"update_data() in {self.__class__.__name__}()") def ignore_attr(self, name: str) -> None: self.__attrs_handled.add(name.lower()) @@ -252,7 +214,7 @@ class SnowrunnerClass: self.load_desc(data.children[name.lower()]) return - if name == "GameData": + if name.lower() == "gamedata": self.load_game_data(data.children[name.lower()]) return @@ -274,9 +236,7 @@ class SnowrunnerClass: ) def loading_finished(self, data: SnowRunnerXNLNode): - attr_set = set(data.attrs.keys()) - if "_template" in attr_set: - attr_set.remove("_template") + attr_set = set(data.attrs.keys()) - {"_template"} if len(attr_set - self.__attrs_handled) > 0: logger.error( f"{self.__class__.__name__}(): Unhandled Attributes: {[attr_set - self.__attrs_handled]}, (Handled: {[self.__attrs_handled]}, Exist: {[attr_set]}" @@ -357,15 +317,18 @@ class SnowrunnerClass: class AddonSlots(SnowrunnerClass): def __init__(self, pak_loader: InitialPakLoader): super().__init__(pak_loader=pak_loader) + self.AllowedCargo = None self.InitialOffset = "(0;0;0)" self.Quantity = 0 self.UseTrailerFrame = False def load_data(self, data: SnowRunnerXNLNode) -> None: + self.load_attr(data, "AllowedCargo") self.load_attr(data, "InitialOffset") self.load_attr(data, "Quantity") self.load_attr(data, "UseTrailerFrame") self.ignore_attr("OffsetStep") + self.ignore_attr("ParentFrame") self.ignore_attr("ParentFrames") self.loading_finished(data) @@ -436,10 +399,10 @@ class Body(SnowrunnerClass): self.load_attr(data, "Friction") self.load_attr(data, "Mass") self.load_attr(data, "Name") - self.load_attr(data, "IsCapsuleCDT") self.load_attr(data, "DamageMult") self.load_attr(data, "RollingFrictionMultiplier") self.load_attr(data, "ImpactType") + self.load_attr(data, "IsCapsuleCDT") self.load_attr(data, "LinearDamping") self.load_attr(data, "AngularDamping") self.load_attr(data, "UseDirtTint") @@ -461,8 +424,17 @@ class Body(SnowrunnerClass): self.load_attr(data, "NetSyncUseGravity") self.load_attr(data, "NoPackWheels") self.ignore_attr("ExplicitParentFrame") + self.ignore_attr("FrictionPreset") + self.ignore_attr("HideDistance") + self.ignore_attr("IsComplexPhysModel") + self.ignore_attr("IsGameAsphalt") + self.ignore_attr("IsStaticSoil") self.ignore_attr("NetSync") self.ignore_attr("ParentFrame") + self.ignore_attr("SCapsuleCdt") + self.ignore_attr("ShadowHideDistance") + self.ignore_attr("template") + self.load_child(data, "Body", always_as_list=True) self.ignore_child("Constraint") self.ignore_child("Motor") @@ -581,16 +553,21 @@ class FuelMass(SnowrunnerClass): class GameData(SnowrunnerClass): def __init__(self, pak_loader: InitialPakLoader): super().__init__(pak_loader=pak_loader) + self.AddonSlots = None self.AddonSockets = None self.CameraPreset = None self.Category = None self.Country = "" self.ExcludeAddons = "" self.ExcludedCargoTypes = "" + self.InstallSlot = None self.InstallSocket = None self.IsCustomizable = False self.IsDoubleTrailer = False self.IsQuest = False + self.LoadType = None + self.LoadAddons = None + self.PackSlotsNumber = 0 self.Price = 0 self.UnlockByRank = -1 self.UnlockByExploration = False @@ -635,29 +612,43 @@ class GameData(SnowrunnerClass): self.load_attr(data, "RecreateOnZoneChange") self.load_attr(data, "TrialsToUnlock") self.load_attr(data, "IncludedCargoTypes") + self.load_attr(data, "PackSlotsNumber") + self.load_attr(data, "LoadAddons") + if "loadtype" in data.attrs: + self.LoadType = self.pak.get_object(SnowrunnerGameObjectType.CARGO_TYPE, data.attrs["loadtype"]) + self.ignore_attr("LoadType") + self.ignore_attr("_noinherit") self.ignore_attr("AddonUnlockByObjective") self.ignore_attr("FrameAlign") self.ignore_attr("FrameAlignOffset") self.ignore_attr("LegacyAlignementTruckAfterGateway") + self.ignore_attr("ManualLoadSpawnOffsetX") + self.ignore_attr("ManualLoadSpawnOffsetY") self.ignore_attr("Recallable") self.ignore_attr("ResetTruckAfterGateway") self.children = data.children self.load_child(data, "AddonSockets", always_as_list=True) + self.load_child(data, "AddonSlots") self.load_child(data, "UiDesc") - if "installsocket" in data.children: - self.InstallSocket = data.children["installsocket"].attrs.get("type", self.InstallSocket) + if self.InstallSocket is None: + self.load_child(data, "InstallSocket") + elif "installsocket" in data.children: + self.InstallSocket.update_data(data.children["installsocket"]) self.ignore_child("InstallSocket") - - self.ignore_child("AddonSlots") + if self.InstallSlot is None: + self.load_child(data, "InstallSlot") + elif "installslot" in data.children: + self.InstallSlot.update_data(data.children["installslot"]) + self.ignore_child("InstallSlot") self.ignore_child("AddonType") self.ignore_child("CenteringAngularVelocity") self.ignore_child("CraneSocket") self.ignore_child("CustomizationCameras") self.ignore_child("GearboxParams") - self.ignore_child("InstallSlot") self.ignore_child("LoadArea") + self.ignore_child("LoadPoint") self.ignore_child("LongLogsAlignTarget") self.ignore_child("RequiredAddon") self.ignore_child("RequiredAddonType") @@ -794,7 +785,7 @@ class InstallSlot(SnowrunnerClass): super().__init__(pak_loader=pak_loader) self.CargoAddonSubtype = "" self.CargoLength = 0 - self.CargoType = "" + self.CargoType = None self.CargoValue = 1 self.ManualLoads = 0 self.NoPackWheels = False @@ -803,7 +794,9 @@ class InstallSlot(SnowrunnerClass): def load_data(self, data: SnowRunnerXNLNode) -> None: self.load_attr(data, "CargoAddonSubtype") self.load_attr(data, "CargoLength") - self.load_attr(data, "CargoType") + if "cargotype" in data.attrs: + self.CargoType = self.pak.get_object(SnowrunnerGameObjectType.CARGO_TYPE, data.attrs["cargotype"]) + self.ignore_attr("CargoType") self.load_attr(data, "CargoValue") self.load_attr(data, "ManualLoads") self.load_attr(data, "NoPackWheels") @@ -823,12 +816,66 @@ class InstallSocket(SnowrunnerClass): self.ignore_attr("Price") # SERIOUSLY, SABER self.ignore_attr("UnlockByRank") # VALIDATE self.ignore_attr("UnlockByExploration") # YOUR INPUTS + self.ignore_attr("CablesPos") self.load_attr(data, "CameraPreset") self.load_attr(data, "Type") self.load_attr(data, "ParentFrame") self.load_attr(data, "Offset") self.loading_finished(data) + def update_data(self, data: SnowRunnerXNLNode) -> None: + self.load_data(data) + + +class ModelBrand(SnowrunnerClass): + def __init__(self, pak_loader: InitialPakLoader): + super().__init__(pak_loader=pak_loader) + self.PhysicsModel = None + + def load_data(self, data: SnowRunnerXNLNode) -> None: + self.ignore_attr("acceptzoneborderdecals") + self.ignore_attr("applynormalinreference") + self.ignore_attr("breaktype") + self.ignore_attr("castnightlightmapshadow") + self.ignore_attr("castdaylightmapshadow") + self.ignore_attr("clipcamera") + self.ignore_attr("clipcamerabridge") + self.ignore_attr("dayshadowoffsetmult") + self.ignore_attr("deactivationphantomext") + self.ignore_attr("deleteonbreak") + self.ignore_attr("disableshadows") + self.ignore_attr("distskippercentage") + self.ignore_attr("dynamicmodel") + self.ignore_attr("dynamicmodeldistactivation") + self.ignore_attr("fence") + self.ignore_attr("inbackground") + self.ignore_attr("instanced") + self.ignore_attr("instancedmodel") + self.ignore_attr("instancedasmodel") + self.ignore_attr("IsComplexPhysModel") + self.ignore_attr("iskeepphysmodelalways") + self.ignore_attr("nightlightingshadowsallowcap") + self.ignore_attr("nosoftcontacts") + self.ignore_attr("skipextrablocks") + self.ignore_attr("usephysicsforinstancing") + + self.load_child(data, "GameData") + self.load_child(data, "PhysicsModel") + self.ignore_child("AnimSubset") + self.ignore_child("LandMark") + self.ignore_child("MeshLod") + self.ignore_child("Occlusion") + self.ignore_child("PreBuild") + self.ignore_child("SnapPoint") + self.ignore_child("StaticLight") + self.ignore_child("StaticLights") + self.ignore_child("Subset") + self.ignore_child("TrackEvents") + self.loading_finished(data) + + def update_data(self, data: SnowRunnerXNLNode) -> None: + self.load_data(data) + class Multiplier(SnowrunnerClass): def __init__(self, pak_loader: InitialPakLoader): @@ -861,6 +908,7 @@ class PhysicsModel(SnowrunnerClass): self.load_attr(data, "_noinherit") self.load_attr(data, "Mesh") self.load_child(data, "Body") + self.ignore_child("Constraint") self.ignore_child("FarmingBoundingBox1") self.ignore_child("FarmingBoundingBox2") self.ignore_child("FarmingBoundingBox3") @@ -869,6 +917,7 @@ class PhysicsModel(SnowrunnerClass): self.ignore_child("FarmingBoundingBox6") self.ignore_child("FarmingBoundingBox7") self.ignore_child("FarmingBoundingBox8") + self.ignore_child("Flare") self.ignore_child("NetSync") self.loading_finished(data) @@ -888,15 +937,19 @@ class RequiredAddon(SnowrunnerClass): class Socket(SnowrunnerClass): def __init__(self, pak_loader: InitialPakLoader): super().__init__(pak_loader=pak_loader) - self.InCockpit = False - self.Names = "" - self.NamesBlock = "" + self.InCockpit: bool = False + self.Names: list[str] = [] + self.NamesBlock: list[str] = [] self.RequiredHitches = None def load_data(self, data: SnowRunnerXNLNode) -> None: self.load_attr(data, "InCockpit") - self.load_attr(data, "Names") - self.load_attr(data, "NamesBlock") + if "names" in data.attrs: + self.Names = [x.strip() for x in data.attrs["names"].split(",")] + self.ignore_attr("Names") + if "namesblock" in data.attrs: + self.NamesBlock = [x.strip() for x in data.attrs["namesblock"].split(",")] + self.ignore_attr("NamesBlock") self.ignore_attr("AddonShift") self.ignore_attr("Dir") self.ignore_attr("Offset") @@ -988,6 +1041,8 @@ class Truck(SnowrunnerClass): self.TruckData = None self.Type = "Truck" self.WaterMass = None + self.__addon_socket_names = None + self.__truck_addon_list = None @property def TireCount(self) -> int: @@ -1030,14 +1085,22 @@ class Truck(SnowrunnerClass): ) def get_addon_socket_names(self) -> list[str]: - return [s.Names for s in [y for x in self.GameData.AddonSockets for y in x.Socket]] + if self.__addon_socket_names is None: + self.__addon_socket_names = [ + n for s in [y for x in self.GameData.AddonSockets for y in x.Socket] for n in s.Names + ] + + return self.__addon_socket_names def get_available_truck_addons(self, category: TruckAddonCategory) -> list[TruckAddon]: - return [ - ta - for ta in self.pak.get_object_list(SnowrunnerGameObjectType.TRUCK_ADDON) - if ta.GameData.InstallSocket in self.get_addon_socket_names() and ta.GameData.Category == category - ] + if self.__truck_addon_list is None: + self.__truck_addon_list = [ + ta + for ta in self.pak.get_object_list(SnowrunnerGameObjectType.TRUCK_ADDON) + if ta.GameData.InstallSocket and ta.GameData.InstallSocket.Type in self.get_addon_socket_names() + ] + + return [x for x in self.__truck_addon_list if x.GameData.Category == category] def get_default_truck_addons(self) -> list[TruckAddon]: return [x.DefaultAddon for x in self.GameData.AddonSockets if x.DefaultAddon is not None] @@ -1632,6 +1695,7 @@ TYPE_CLASS_MATRIX = { "gearboxvariants": GearboxVariants, "installslot": InstallSlot, "installsocket": InstallSocket, + "modelbrand": ModelBrand, "multiplier": Multiplier, "physicsmodel": PhysicsModel, "requiredaddon": RequiredAddon, @@ -1674,6 +1738,7 @@ class SnowrunnerGameObjectType(str, Enum): GEARBOX_VARIANTS = "gearboxvariants" INSTALL_SLOT = "installslot" INSTALL_SOCKET = "installsocket" + MODEL_BRAND = "modelbrand" MULTIPLIER = "multiplier" PHYSICS_MODEL = "physicsmodel" REQUIRED_ADDON = "requiredaddon" @@ -1712,6 +1777,7 @@ class SnowrunnerGameObjectType(str, Enum): class InitialPakLoader: game_data_load_order = [ "cargotype", + "modelbrand", "enginevariants", "gearboxvariants", "suspensionsetvariants", @@ -1754,7 +1820,6 @@ class InitialPakLoader: "materialtype", "mainheadlight", "model", - "modelbrand", "motor", "mudguard", "mudtype", @@ -1861,28 +1926,23 @@ class InitialPakLoader: if not isinstance(sub_data, list) and "_template" in sub_data.attrs: defer.append((tp_name, sub_data)) continue + + template = self.load_class(object_type=object_type, data=sub_data, dlc_name=dlc_name) + if isinstance(template, list): + template = template[-1] if local: - self.templates_local[category][tp_name] = self.load_class( - object_type=object_type, - data=sub_data, - dlc_name=dlc_name, - ) + self.templates_local[category][tp_name] = template else: - self.templates[category][tp_name] = self.load_class( - object_type=object_type, data=sub_data, dlc_name=dlc_name - ) + self.templates[category][tp_name] = template if defer: for tp_name, sub_data in defer: + template = self.load_class(object_type=object_type, data=sub_data, dlc_name=dlc_name) + if isinstance(template, list): + template = template[-1] if local: - self.templates_local[category][tp_name] = self.load_class( - object_type=object_type, - data=sub_data, - dlc_name=dlc_name, - ) + self.templates_local[category][tp_name] = template else: - self.templates[category][tp_name] = self.load_class( - object_type=object_type, data=sub_data, dlc_name=dlc_name - ) + self.templates[category][tp_name] = template elif category in self.ignore_categories: pass # Ignore for now @@ -1892,17 +1952,18 @@ class InitialPakLoader: def get_template(self, object_type: SnowrunnerGameObjectType, name: str) -> SnowrunnerClass: name = name.lower() - if object_type in self.templates_local and name in self.templates_local[object_type]: + if name in self.templates_local[object_type]: return self.templates_local[object_type][name] - elif object_type in self.templates and name in self.templates[object_type]: + elif name in self.templates[object_type]: return self.templates[object_type][name] else: - logger.error(f"Unknown Template requested: {name} in {object_type.name}") - sys.exit(1) + logger.warning(f"Unknown Template requested: {name} in {object_type.name}") + return SnowrunnerClass(pak_loader=self) def get_object( self, object_type: SnowrunnerGameObjectType, name: str, lazy: bool = False ) -> SnowrunnerClass | None: + FAIL_NAMES = ["CargoBarrelsMasut", "CargoServiceSparePartsTown", "LongLogs", "MediumLogs", "ShortLogs"] # name = name.lower() if object_type in self.game_data: if name in self.game_data[object_type]: @@ -1912,7 +1973,7 @@ class InitialPakLoader: if getattr(v, "Name", "") == name: return v - if lazy: + if lazy or name in FAIL_NAMES: return None logger.error(f"Unknown Game Data Object requested: {name} in {object_type.name}") @@ -1921,6 +1982,9 @@ class InitialPakLoader: def get_object_list(self, object_type: SnowrunnerGameObjectType) -> list: return list(self.game_data[object_type].values()) + def get_object_list_with_ref(self, object_type: SnowrunnerGameObjectType) -> list[tuple[str, SnowrunnerClass]]: + return list(self.game_data[object_type].items()) + def get_sorted_object_list( self, object_type: SnowrunnerGameObjectType, language: LanguageLoader ) -> list[tuple[str, SnowrunnerClass]]: diff --git a/output.py b/output.py index 8879051..1ae7698 100644 --- a/output.py +++ b/output.py @@ -6,8 +6,6 @@ from loader import ( LanguageLoader, SnowrunnerGameObjectType, TruckAddonCategory, - GameData, - SnowrunnerClass, ) from logging import getLogger @@ -32,6 +30,8 @@ def get_head(language: LanguageLoader, category: str, name: str | None = None) -
@@ -423,7 +423,7 @@ def write_frame_addons(pak: InitialPakLoader, destination_directory: str, langua available_trucks = [ x for x in pak.get_sorted_object_list(SnowrunnerGameObjectType.TRUCK, language=language) - if x[1].Type == "Truck" and frame_addon.GameData.InstallSocket in x[1].get_addon_socket_names() + if x[1].Type == "Truck" and frame_addon.GameData.InstallSocket.Type in x[1].get_addon_socket_names() ] if not available_trucks: continue @@ -459,6 +459,118 @@ def write_frame_addons(pak: InitialPakLoader, destination_directory: str, langua ) +def write_trailers(pak: InitialPakLoader, destination_directory: str, language: LanguageLoader): + index_content = "

Trailers

\n" + index_content += f" \n" + index_content += ( + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + ) + index_content += f" \n" + + for ref, trailer in pak.get_sorted_object_list(SnowrunnerGameObjectType.TRUCK, language=language): + if trailer.Type != "Trailer": + continue + + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + if trailer.GameData.AddonSlots is None: + index_content += f" \n" + else: + if isinstance(trailer.GameData.AddonSlots, list): + index_content += f" \n" + else: + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + + index_content += f" \n" + index_content += f"
NamePriceUnlock ByQuestMassHitchCargo SlotsRepair PointsSpare WheelsFuel CapacityWater Capacity
{trailer.get_translated_ui_name(language)}{trailer.Price}{trailer.GameData.get_unlock_str()}{trailer.GameData.IsQuest}{trailer.PhysicsModel.Weight}kg{trailer.GameData.InstallSocket.Type} {sum(x.Quantity for x in trailer.GameData.AddonSlots)}{trailer.GameData.AddonSlots.Quantity}{trailer.TruckData.RepairsCapacity if trailer.TruckData is not None and trailer.TruckData.RepairsCapacity > 0 else ' '}{trailer.TruckData.WheelRepairsCapacity if trailer.TruckData is not None and trailer.TruckData.WheelRepairsCapacity > 0 else ' '}{trailer.TruckData.FuelCapacity if trailer.TruckData is not None and trailer.TruckData.FuelCapacity > 0 else ' '}{trailer.TruckData.WaterCapacity if trailer.TruckData is not None and trailer.TruckData.WaterCapacity > 0 else ' '}
\n" + index_content += " \n" + + write_html( + os.path.join(destination_directory, "index.html"), + language=language, + content=index_content, + category="Trailers", + ) + + +def write_cargo_types(pak: InitialPakLoader, destination_directory: str, language: LanguageLoader): + index_content = "

Trailers

\n" + index_content += f" \n" + index_content += ( + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + f" \n" + ) + index_content += f" \n" + + for _, cargo_type in pak.get_sorted_object_list(SnowrunnerGameObjectType.CARGO_TYPE, language=language): + install_addons = [ + x + for x in pak.get_object_list(SnowrunnerGameObjectType.TRUCK_ADDON) + if x.GameData.InstallSlot is not None and x.GameData.InstallSlot.CargoType == cargo_type + ] + if not install_addons: + continue + + if len(install_addons) > 1: + # FIXME: These are Logs + continue + + install_addon = install_addons[0] + if install_addon.PhysicsModel is None: + continue # FIXME: Water, Fuel + + model_brands = [ + x + for x in pak.get_object_list(SnowrunnerGameObjectType.MODEL_BRAND) + if x.GameData is not None and x.GameData.LoadType == cargo_type + ] + model_brand = model_brands[-1] + + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + index_content += f" \n" + + index_content += f" \n" + index_content += f"
NameMass (unpacked)Mass (packed)Cargo Slots
{cargo_type.get_translated_ui_name(language)}{model_brand.PhysicsModel.Weight}{install_addon.PhysicsModel.Weight}{install_addon.GameData.InstallSlot.CargoLength}
\n" + index_content += " \n" + + write_html( + os.path.join(destination_directory, "index.html"), + language=language, + content=index_content, + category="Cargo Types", + ) + + def create_html(pak: InitialPakLoader, destination_directory: str = "output/"): if not os.path.exists(destination_directory): os.mkdir(destination_directory) @@ -479,3 +591,13 @@ def create_html(pak: InitialPakLoader, destination_directory: str = "output/"): if not os.path.exists(frame_addon_path): os.mkdir(frame_addon_path) write_frame_addons(pak=pak, destination_directory=frame_addon_path, language=language_data) + + trailers_path = os.path.join(destination_directory, language_name, "trailers") + if not os.path.exists(trailers_path): + os.mkdir(trailers_path) + write_trailers(pak=pak, destination_directory=trailers_path, language=language_data) + + cargo_types_path = os.path.join(destination_directory, language_name, "cargo_types") + if not os.path.exists(cargo_types_path): + os.mkdir(cargo_types_path) + write_cargo_types(pak=pak, destination_directory=cargo_types_path, language=language_data)