trucks and addons
This commit is contained in:
parent
1780362b90
commit
fe6e55a891
64
constants.py
Normal file
64
constants.py
Normal file
@ -0,0 +1,64 @@
|
||||
COUNTRYS = {}
|
||||
|
||||
DLC_NAMES = {
|
||||
None: "Base Game",
|
||||
"dlc_1_1": "Season 1: Search & Recover<br>Kola Peninsula, Russia",
|
||||
"dlc_1_2": "Clasico Pack",
|
||||
"dlc_2_1": "Season 2: Explore & Expand<br>Yukon, Canada",
|
||||
"dlc_2_2": "Season 2: Explore & Expand",
|
||||
"dlc_3": "Season 3: Locate & Deliver<br>Wisconsin, USA",
|
||||
"DLC_trial_6": "GMC Brigadier",
|
||||
"dlc_4": "Season 4: New Frontiers<br>Amur, Russia",
|
||||
"dlc_4_1": "Anniversary DLC",
|
||||
"dlc_5": "Season 5: Build & Dispatch<br>Don, Russia",
|
||||
"dlc_6": "Season 6: Haul & Hustle<br>Maine, USA",
|
||||
"dlc_7": "Season 7: Compete & Conquer<br>Tennessee, USA",
|
||||
"dlc_8": "Season 8: Grand Harvest<br>Belozersk Glades, Central Asia",
|
||||
"dlc_9": "Season 9: Renew & Rebuild<br>Ontario, Canada",
|
||||
"dlc_10": "Season 10: Fix & Connect<br>British Columbia, Canada",
|
||||
"dlc_11": "Season 11: Light & Cameras<br>Scandinavia, Northern Europe",
|
||||
"dlc_12": "Season 12: Public Energy<br>North Carolina, USA",
|
||||
"dlc_12_5": "The Atom",
|
||||
"dlc_13": "Season 13: Dig & Drill<br>Almaty Region, Central Asia",
|
||||
"dlc_13_5": "Rezvani Tank",
|
||||
"dlc_14": "Season 14: Reap & Sow<br>Austria, Central Europe",
|
||||
"dlc_14_1": "Kenworth Dual Pack",
|
||||
"dlc_14_2": "Mack Dual Pack",
|
||||
"dlc_15": "Season 15: Oil & Dirt<br>Quebec, Canada",
|
||||
"dlc_15_5": "Freightliner & Western Star Dual Pack",
|
||||
"dlc_16": "Season 16: High Voltage<br>Washington, USA",
|
||||
"dlc_16_5": "Dragon Wagons Dual Pack",
|
||||
"dlc_17": "Season 17: Repair & Rescue<br>Zurdania, Western Asia",
|
||||
"dlc_17_5": "Mercedes-Benz Trucks Dual Pack 1",
|
||||
"dlc_18": "Season 18: Patch & Power<br>Chiapas, Mexico",
|
||||
"dlc_navistar": "Navistart 5000-MV Tractor",
|
||||
"dlc_high_roller": "High Roller Pack",
|
||||
"dlc_ws_49x": "Western Star 49X",
|
||||
"dlc_tatra": "TATRA Dual Pack",
|
||||
"dlc_jeep": "Jeep Dual Pack",
|
||||
"dlc_crocodile": "Crocodile Pack",
|
||||
"dlc_land_rover": "Land Rover Dual Pack",
|
||||
"dlc_ws_wolf": "Western Star Wolf Pack",
|
||||
"dlc_rezvani": "Rezvani Hercules",
|
||||
"dlc_mastodon": "The Mastodon",
|
||||
"dlc_w990": "Kenworth W990",
|
||||
}
|
||||
|
||||
DLC_OVERRIDES = {
|
||||
"UI_VEHICLE_NAVISTAR_5000_NAME": "dlc_navistar",
|
||||
"UI_VEHICLE_KHAN_39_MARSHALL_NAME": "dlc_high_roller",
|
||||
"UI_VEHICLE_WESTERN_STAR_49X_NAME": "dlc_ws_49x",
|
||||
"UI_VEHICLE_TATRA_805_NAME": "dlc_tatra",
|
||||
"UI_VEHICLE_TATRA_T813_NAME": "dlc_tatra",
|
||||
"UI_VEHICLE_JEEP_CJ7_RENEGADE_NAME": "dlc_jeep",
|
||||
"UI_VEHICLE_JEEP_WRANGLER_NAME": "dlc_jeep",
|
||||
"UI_VEHICLE_STEP_3364_CROCODILE_NAME": "dlc_crocodile",
|
||||
"UI_VEHICLE_LAND_ROVER_DEFENDER_110_NAME": "dlc_land_rover",
|
||||
"UI_VEHICLE_LAND_ROVER_DEFENDER_90_NAME": "dlc_land_rover",
|
||||
"UI_VEHICLE_WESTERN_STAR_57X_NAME": "dlc_ws_wolf",
|
||||
"UI_VEHICLE_WESTERN_STAR_NF1424_NAME": "dlc_ws_wolf",
|
||||
"UI_VEHICLE_WESTERN_STAR_NF1430_NAME": "dlc_ws_wolf",
|
||||
"UI_VEHICLE_REZVANI_NAME": "dlc_rezvani",
|
||||
"UI_VEHICLE_ZIKZ_612H_MASTODONT_NAME": "dlc_mastodon",
|
||||
"UI_VEHICLE_KENWORTH_W990_NAME": "dlc_w990",
|
||||
}
|
||||
397
loader.py
397
loader.py
@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
from constants import DLC_OVERRIDES
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from enum import Enum
|
||||
from html import parser as html_parser
|
||||
@ -58,6 +59,37 @@ locatestarttagend_tolerant = re.compile(
|
||||
endtagfind = re.compile(r"</\s*([a-zA-Z_][-.a-zA-Z0-9:_]*)\s*>")
|
||||
|
||||
|
||||
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("~")),
|
||||
@ -157,11 +189,12 @@ class SnowrunnerClass:
|
||||
self.pak = pak_loader
|
||||
self.UiDesc = ""
|
||||
self.UiName = ""
|
||||
self.Price = -1
|
||||
self.Price = 0
|
||||
self.UnlockByExploration = False
|
||||
self.UnlockByObjective = False
|
||||
self.UnlockByRank = -1
|
||||
self.GameData = None
|
||||
self.DLC = None
|
||||
|
||||
def load_desc(self, data: SnowRunnerXNLNode):
|
||||
if "region:default" in data.children:
|
||||
@ -174,7 +207,10 @@ class SnowrunnerClass:
|
||||
self.UiName = data.get_attr("uiname")
|
||||
|
||||
def load_game_data(self, data: SnowRunnerXNLNode):
|
||||
self.GameData = self.pak.load_class(SnowrunnerGameObjectType.GAME_DATA, data)
|
||||
if self.GameData is None:
|
||||
self.GameData = self.pak.load_class(SnowrunnerGameObjectType.GAME_DATA, data, dlc_name=self.DLC)
|
||||
else:
|
||||
self.GameData.update_data(data)
|
||||
self.Price = self.GameData.Price
|
||||
self.UnlockByExploration = self.GameData.UnlockByExploration
|
||||
self.UnlockByObjective = self.GameData.UnlockByObjective
|
||||
@ -201,7 +237,11 @@ class SnowrunnerClass:
|
||||
self.__children_handled.add(name.lower())
|
||||
|
||||
def load_child(
|
||||
self, data: SnowRunnerXNLNode, name: str, object_type_override: SnowrunnerGameObjectType | None = None
|
||||
self,
|
||||
data: SnowRunnerXNLNode,
|
||||
name: str,
|
||||
object_type_override: SnowrunnerGameObjectType | None = None,
|
||||
always_as_list: bool = False,
|
||||
):
|
||||
self.__children_handled.add(name.lower())
|
||||
|
||||
@ -216,11 +256,22 @@ class SnowrunnerClass:
|
||||
self.load_game_data(data.children[name.lower()])
|
||||
return
|
||||
|
||||
if always_as_list and not isinstance(data.children[name.lower()], list):
|
||||
data.children[name.lower()] = [data.children[name.lower()]]
|
||||
|
||||
object_type = SnowrunnerGameObjectType(name.lower()) if object_type_override is None else object_type_override
|
||||
if isinstance(data.children[name.lower()], list):
|
||||
setattr(self, name, [self.pak.load_class(object_type, x) for x in data.children[name.lower()]])
|
||||
setattr(
|
||||
self,
|
||||
name,
|
||||
[self.pak.load_class(object_type, x, dlc_name=self.DLC) for x in data.children[name.lower()]],
|
||||
)
|
||||
else:
|
||||
setattr(self, name, self.pak.load_class(object_type, data.children[name.lower()]))
|
||||
setattr(
|
||||
self,
|
||||
name,
|
||||
self.pak.load_class(object_type, data.children[name.lower()], dlc_name=self.DLC),
|
||||
)
|
||||
|
||||
def loading_finished(self, data: SnowRunnerXNLNode):
|
||||
attr_set = set(data.attrs.keys())
|
||||
@ -239,6 +290,9 @@ class SnowrunnerClass:
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if self.UiName in DLC_OVERRIDES:
|
||||
self.DLC = DLC_OVERRIDES[self.UiName]
|
||||
|
||||
def get_translated_ui_desc(self, language: LanguageLoader) -> str:
|
||||
if self.UiDesc in language:
|
||||
return language[self.UiDesc]
|
||||
@ -305,22 +359,30 @@ class AddonSlots(SnowrunnerClass):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.InitialOffset = "(0;0;0)"
|
||||
self.Quantity = 0
|
||||
self.UseTrailerFrame = False
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "InitialOffset")
|
||||
self.load_attr(data, "Quantity")
|
||||
self.load_attr(data, "UseTrailerFrame")
|
||||
self.ignore_attr("OffsetStep")
|
||||
self.ignore_attr("ParentFrames")
|
||||
self.loading_finished(data)
|
||||
|
||||
|
||||
class AddonSockets(SnowrunnerClass):
|
||||
def __init__(self, pak_loader: InitialPakLoader):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.DefaultAddon = ""
|
||||
self.DefaultAddon = None
|
||||
self.Socket = None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "DefaultAddon")
|
||||
self.load_child(data, "Socket")
|
||||
if "defaultaddon" in data.attrs:
|
||||
self.DefaultAddon = self.pak.get_object(
|
||||
object_type=SnowrunnerGameObjectType.TRUCK_ADDON, name=data.attrs["defaultaddon"], lazy=True
|
||||
)
|
||||
self.ignore_attr("defaultaddon")
|
||||
self.load_child(data, "Socket", always_as_list=True)
|
||||
self.ignore_attr("ParentFrame")
|
||||
self.ignore_attr("RequiredAddonIfNoConflicts")
|
||||
self.loading_finished(data)
|
||||
@ -364,6 +426,10 @@ class Body(SnowrunnerClass):
|
||||
self.ModelFrame = None
|
||||
self.Body = []
|
||||
|
||||
@property
|
||||
def Weight(self) -> int:
|
||||
return self.Mass + sum(x.Weight for x in self.Body)
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "AllowedPenetrationDepth")
|
||||
self.load_attr(data, "NoSoftContacts")
|
||||
@ -397,8 +463,7 @@ class Body(SnowrunnerClass):
|
||||
self.ignore_attr("ExplicitParentFrame")
|
||||
self.ignore_attr("NetSync")
|
||||
self.ignore_attr("ParentFrame")
|
||||
|
||||
self.load_child(data, "Body")
|
||||
self.load_child(data, "Body", always_as_list=True)
|
||||
self.ignore_child("Constraint")
|
||||
self.ignore_child("Motor")
|
||||
self.ignore_child("SideMirror")
|
||||
@ -489,18 +554,17 @@ class Engine(SnowrunnerClass):
|
||||
class EngineVariants(SnowrunnerClass):
|
||||
def __init__(self, pak_loader: InitialPakLoader):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.Engines = []
|
||||
self.Engine = []
|
||||
|
||||
def get_engine(self, name: str) -> Engine | None:
|
||||
for e in self.Engine:
|
||||
if e.Name == name:
|
||||
return e
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
if "engine" in data.children:
|
||||
if isinstance(data.children["engine"], list):
|
||||
self.Engines = [
|
||||
self.pak.load_class(SnowrunnerGameObjectType.ENGINE, x) for x in data.children["engine"]
|
||||
]
|
||||
else:
|
||||
self.Engines = [self.pak.load_class(SnowrunnerGameObjectType.ENGINE, data.children["engine"])]
|
||||
self.ignore_child("Engine")
|
||||
|
||||
self.load_child(data, "Engine", always_as_list=True)
|
||||
self.loading_finished(data)
|
||||
|
||||
|
||||
@ -523,10 +587,11 @@ class GameData(SnowrunnerClass):
|
||||
self.Country = ""
|
||||
self.ExcludeAddons = ""
|
||||
self.ExcludedCargoTypes = ""
|
||||
self.InstallSocket = None
|
||||
self.IsCustomizable = False
|
||||
self.IsDoubleTrailer = False
|
||||
self.IsQuest = False
|
||||
self.Price = -1
|
||||
self.Price = 0
|
||||
self.UnlockByRank = -1
|
||||
self.UnlockByExploration = False
|
||||
self.UnlockByObjective = False
|
||||
@ -544,7 +609,9 @@ class GameData(SnowrunnerClass):
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "CameraPreset")
|
||||
self.load_attr(data, "Category")
|
||||
if "category" in data.attrs:
|
||||
self.Category = TruckAddonCategory(data.attrs["category"])
|
||||
self.ignore_attr("Category")
|
||||
self.load_attr(data, "Country")
|
||||
self.load_attr(data, "ExcludeAddons")
|
||||
self.load_attr(data, "ExcludedCargoTypes")
|
||||
@ -577,8 +644,12 @@ class GameData(SnowrunnerClass):
|
||||
self.ignore_attr("ResetTruckAfterGateway")
|
||||
|
||||
self.children = data.children
|
||||
self.load_child(data, "AddonSockets")
|
||||
self.load_child(data, "AddonSockets", always_as_list=True)
|
||||
self.load_child(data, "UiDesc")
|
||||
if "installsocket" in data.children:
|
||||
self.InstallSocket = data.children["installsocket"].attrs.get("type", self.InstallSocket)
|
||||
self.ignore_child("InstallSocket")
|
||||
|
||||
self.ignore_child("AddonSlots")
|
||||
self.ignore_child("AddonType")
|
||||
self.ignore_child("CenteringAngularVelocity")
|
||||
@ -586,7 +657,6 @@ class GameData(SnowrunnerClass):
|
||||
self.ignore_child("CustomizationCameras")
|
||||
self.ignore_child("GearboxParams")
|
||||
self.ignore_child("InstallSlot")
|
||||
self.ignore_child("InstallSocket")
|
||||
self.ignore_child("LoadArea")
|
||||
self.ignore_child("LongLogsAlignTarget")
|
||||
self.ignore_child("RequiredAddon")
|
||||
@ -617,6 +687,9 @@ class GameData(SnowrunnerClass):
|
||||
self.ignore_child("longlogsaligndata")
|
||||
self.loading_finished(data)
|
||||
|
||||
def update_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_data(data)
|
||||
|
||||
|
||||
class Gear(SnowrunnerClass):
|
||||
def __init__(self, pak_loader: InitialPakLoader):
|
||||
@ -645,7 +718,7 @@ class Gearbox(SnowrunnerClass):
|
||||
self.GearboxParams = None
|
||||
self.ReverseGear = None
|
||||
self.HighGear = None
|
||||
self.Gears = []
|
||||
self.Gear = []
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "AWDConsumptionModifier")
|
||||
@ -660,10 +733,12 @@ class Gearbox(SnowrunnerClass):
|
||||
self.load_child(data, "GameData")
|
||||
if "gamedata" in data.children:
|
||||
self.load_child(data.children["gamedata"], "GearboxParams")
|
||||
self.load_child(data, "Gear")
|
||||
self.load_child(data, "Gear", always_as_list=True)
|
||||
self.load_child(data, "ReverseGear", object_type_override=SnowrunnerGameObjectType.GEAR)
|
||||
self.load_child(data, "HighGear", object_type_override=SnowrunnerGameObjectType.GEAR)
|
||||
|
||||
self.Gear = list(sorted(self.Gear, key=lambda gear: gear.AngVel))
|
||||
|
||||
self.loading_finished(data)
|
||||
|
||||
|
||||
@ -690,8 +765,15 @@ class GearboxVariants(SnowrunnerClass):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.Gearbox = []
|
||||
|
||||
def get_gearbox(self, name: str) -> Gearbox | None:
|
||||
for e in self.Gearbox:
|
||||
if e.Name == name:
|
||||
return e
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_child(data, "Gearbox")
|
||||
self.load_child(data, "Gearbox", always_as_list=True)
|
||||
self.loading_finished(data)
|
||||
|
||||
def update_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
@ -700,7 +782,7 @@ class GearboxVariants(SnowrunnerClass):
|
||||
|
||||
for orig, upd in zip_longest(self.Gearbox, data.children["gearbox"]):
|
||||
if orig is None:
|
||||
self.Gearbox.append(self.pak.load_class(SnowrunnerGameObjectType.GEARBOX, upd))
|
||||
self.Gearbox.append(self.pak.load_class(SnowrunnerGameObjectType.GEARBOX, upd, dlc_name=self.DLC))
|
||||
elif upd is None:
|
||||
return
|
||||
else:
|
||||
@ -766,6 +848,15 @@ class PhysicsModel(SnowrunnerClass):
|
||||
self.Mesh = ""
|
||||
self.Body = None
|
||||
|
||||
@property
|
||||
def Weight(self) -> int:
|
||||
if not self.Body:
|
||||
return 0
|
||||
if isinstance(self.Body, list):
|
||||
return sum(x.Weight for x in self.Body)
|
||||
else:
|
||||
return self.Body.Weight
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "_noinherit")
|
||||
self.load_attr(data, "Mesh")
|
||||
@ -871,8 +962,15 @@ class SuspensionSetVariants(SnowrunnerClass):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.SuspensionSet = []
|
||||
|
||||
def get_suspension_set(self, name: str) -> SuspensionSet | None:
|
||||
for e in self.SuspensionSet:
|
||||
if e.Name == name:
|
||||
return e
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_child(data, "SuspensionSet")
|
||||
self.load_child(data, "SuspensionSet", always_as_list=True)
|
||||
self.loading_finished(data)
|
||||
|
||||
|
||||
@ -891,6 +989,98 @@ class Truck(SnowrunnerClass):
|
||||
self.Type = "Truck"
|
||||
self.WaterMass = None
|
||||
|
||||
@property
|
||||
def TireCount(self) -> int:
|
||||
return len(self.TruckData.Wheels.Wheel)
|
||||
|
||||
@property
|
||||
def TireCountFront(self) -> int:
|
||||
return len([x for x in self.TruckData.Wheels.Wheel if x.Location == "front"])
|
||||
|
||||
@property
|
||||
def TireCountRear(self) -> int:
|
||||
return len([x for x in self.TruckData.Wheels.Wheel if x.Location == "rear"])
|
||||
|
||||
@property
|
||||
def DriveTrain(self) -> str:
|
||||
undriven_axles = len([x for x in self.TruckData.Wheels.Wheel if x.Torque == "none"])
|
||||
return f"{self.TireCount}x{self.TireCount - undriven_axles}"
|
||||
|
||||
@property
|
||||
def Weight(self) -> int:
|
||||
wheel_mass = (
|
||||
self.TruckData.Wheels.DefaultTire.Mass * self.TireCountFront
|
||||
+ self.TruckData.Wheels.DefaultTire.Mass
|
||||
* self.TruckData.Wheels.DefaultTire.RearMassScale
|
||||
* self.TireCountRear
|
||||
)
|
||||
default_addon_mass = sum(x.PhysicsModel.Weight for x in self.get_default_truck_addons())
|
||||
|
||||
return int(self.PhysicsModel.Weight + wheel_mass + default_addon_mass)
|
||||
|
||||
def get_shop_price(self) -> int:
|
||||
return (
|
||||
self.Price
|
||||
+ self.TruckData.DefaultEngine.Price
|
||||
+ self.TruckData.DefaultGearbox.Price
|
||||
+ self.TruckData.DefaultSuspension.Price
|
||||
+ self.TruckData.DefaultWinch.Price
|
||||
+ self.TruckData.Wheels.DefaultTire.Price
|
||||
+ sum(x.DefaultAddon.Price for x in self.GameData.AddonSockets if x.DefaultAddon is not None)
|
||||
)
|
||||
|
||||
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]]
|
||||
|
||||
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
|
||||
]
|
||||
|
||||
def get_default_truck_addons(self) -> list[TruckAddon]:
|
||||
return [x.DefaultAddon for x in self.GameData.AddonSockets if x.DefaultAddon is not None]
|
||||
|
||||
@property
|
||||
def AWDInfo(self) -> TruckInfo:
|
||||
torques = {x.Torque.lower() for x in self.TruckData.Wheels.Wheel}
|
||||
if len(torques) == 1 and "default" in torques:
|
||||
return TruckInfo.ALWAYS
|
||||
elif "connectable" in torques:
|
||||
return TruckInfo.CAPABLE
|
||||
elif "full" in torques:
|
||||
return TruckInfo.SWITCHABLE
|
||||
elif "none" in torques:
|
||||
return TruckInfo.NOT_AVAILABLE
|
||||
else:
|
||||
raise ValueError(f"Truck {self.UiName} AWD Wheel Torques {torques} is not supported")
|
||||
|
||||
@property
|
||||
def DiffLockInfo(self) -> TruckInfo:
|
||||
if self.TruckData.DiffLockType == "Always":
|
||||
return TruckInfo.ALWAYS
|
||||
elif (
|
||||
self.TruckData.DiffLockType == "Installed"
|
||||
or self.TruckData.DiffLockType == "Switchable"
|
||||
or self.TruckData.DiffLockType == "Connected"
|
||||
or self.TruckData.DiffLockType == "Uninstalled"
|
||||
or self.TruckData.DiffLockType == "None"
|
||||
): # DiffLockType seems to have no meaning. At all. We have to figure it out ourselfs.
|
||||
diff_lock_addons = self.get_available_truck_addons(TruckAddonCategory.DIFF_LOCK)
|
||||
if len(diff_lock_addons) == 0:
|
||||
return TruckInfo.NOT_AVAILABLE
|
||||
elif len(diff_lock_addons) == 1:
|
||||
return TruckInfo.SWITCHABLE
|
||||
else:
|
||||
installed_addon = list(set(diff_lock_addons) & set(self.get_default_truck_addons()))
|
||||
if installed_addon[0].TruckData.DiffLockInstalled:
|
||||
return TruckInfo.SWITCHABLE
|
||||
else:
|
||||
return TruckInfo.CAPABLE
|
||||
else:
|
||||
raise ValueError(f"Truck {self.UiName} DiffLockType {self.TruckData.DiffLockType} is not supported")
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "AttachType")
|
||||
self.load_attr(data, "IsDeprecated")
|
||||
@ -944,6 +1134,7 @@ class TruckAddon(SnowrunnerClass):
|
||||
self.PhysicsModel = None
|
||||
self.TruckData = None
|
||||
self.AddonType = None
|
||||
self.AddonSlots = None
|
||||
self.RequiredAddonType = None
|
||||
self.IsChassisFullOcclusion = False
|
||||
self.FuelMass = None
|
||||
@ -966,7 +1157,7 @@ class TruckAddon(SnowrunnerClass):
|
||||
if "installsocket" in data.children["gamedata"].children:
|
||||
self.load_child(data.children["gamedata"], "InstallSocket")
|
||||
if "addonslots" in data.children["gamedata"].children:
|
||||
self.load_child(data.children["gamedata"], "AddonType")
|
||||
self.load_child(data.children["gamedata"], "AddonSlots")
|
||||
if "addontype" in data.children["gamedata"].children:
|
||||
self.load_child(data.children["gamedata"], "AddonType")
|
||||
if "requiredaddon" in data.children["gamedata"].children:
|
||||
@ -1007,12 +1198,25 @@ class TruckAddon(SnowrunnerClass):
|
||||
|
||||
|
||||
class TruckData(SnowrunnerClass):
|
||||
truck_types = {
|
||||
"HEAVY": "CODEX_TYPE_TRUCK_6_HEADER",
|
||||
"HEAVY_DUTY": "CODEX_TYPE_TRUCK_4_HEADER",
|
||||
"SCOUT": "CODEX_TYPE_TRUCK_3_HEADER",
|
||||
"HIGHWAY": "CODEX_TYPE_TRUCK_2_HEADER",
|
||||
"OFFROAD": "CODEX_TYPE_TRUCK_5_HEADER",
|
||||
}
|
||||
|
||||
def __init__(self, pak_loader: InitialPakLoader):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.AllWheelDriveInstalled = False
|
||||
self.BackSteerSpeed = 0.0
|
||||
self.CompatibleWheels = []
|
||||
self.Damage = None
|
||||
self.DefaultEngine = None
|
||||
self.DefaultGearbox = None
|
||||
self.DefaultSuspension = None
|
||||
self.DefaultTire = None
|
||||
self.DefaultWinch = None
|
||||
self.DiffLockInstalled = False
|
||||
self.DiffLockType = ""
|
||||
self.Engines = []
|
||||
@ -1041,12 +1245,12 @@ class TruckData(SnowrunnerClass):
|
||||
self.load_attr(data, "Responsiveness")
|
||||
self.load_attr(data, "SetHandbrakeOnWinchRelease")
|
||||
self.load_attr(data, "SteerSpeed")
|
||||
self.load_attr(data, "TruckType")
|
||||
if "trucktype" in data.attrs:
|
||||
self.TruckType = self.truck_types[data.attrs["trucktype"]]
|
||||
self.ignore_attr("trucktype")
|
||||
self.load_attr(data, "WaterCapacity")
|
||||
self.load_attr(data, "WheelRepairsCapacity")
|
||||
self.load_child(data, "CompatibleWheels")
|
||||
if self.CompatibleWheels and not isinstance(self.CompatibleWheels, list):
|
||||
self.CompatibleWheels = [self.CompatibleWheels]
|
||||
self.load_child(data, "CompatibleWheels", always_as_list=True)
|
||||
self.load_child(data, "Damage")
|
||||
self.load_child(data, "Wheels")
|
||||
|
||||
@ -1058,6 +1262,9 @@ class TruckData(SnowrunnerClass):
|
||||
self.pak.get_object(object_type=SnowrunnerGameObjectType.ENGINE_VARIANTS, name=n.strip())
|
||||
for n in data.children["enginesocket"].attrs["type"].split(",")
|
||||
]
|
||||
self.DefaultEngine = list(
|
||||
{x.get_engine(data.children["enginesocket"].attrs["default"]) for x in self.Engines} - {None}
|
||||
)[0]
|
||||
self.ignore_child("EngineSocket")
|
||||
|
||||
if "gearboxsocket" in data.children:
|
||||
@ -1065,6 +1272,9 @@ class TruckData(SnowrunnerClass):
|
||||
self.pak.get_object(object_type=SnowrunnerGameObjectType.GEARBOX_VARIANTS, name=n.strip())
|
||||
for n in data.children["gearboxsocket"].attrs["type"].split(",")
|
||||
]
|
||||
self.DefaultGearbox = list(
|
||||
{x.get_gearbox(data.children["gearboxsocket"].attrs["default"]) for x in self.Gearboxes} - {None}
|
||||
)[0]
|
||||
self.ignore_child("GearboxSocket")
|
||||
|
||||
if "suspensionsocket" in data.children:
|
||||
@ -1072,6 +1282,10 @@ class TruckData(SnowrunnerClass):
|
||||
self.pak.get_object(object_type=SnowrunnerGameObjectType.SUSPENSION_SET_VARIANTS, name=n.strip())
|
||||
for n in data.children["suspensionsocket"].attrs["type"].split(",")
|
||||
]
|
||||
self.DefaultSuspension = list(
|
||||
{x.get_suspension_set(data.children["suspensionsocket"].attrs["default"]) for x in self.Suspensions}
|
||||
- {None}
|
||||
)[0]
|
||||
self.ignore_child("SuspensionSocket")
|
||||
|
||||
if "winchupgradesocket" in data.children:
|
||||
@ -1079,6 +1293,9 @@ class TruckData(SnowrunnerClass):
|
||||
self.pak.get_object(object_type=SnowrunnerGameObjectType.WINCH_VARIANTS, name=n.strip())
|
||||
for n in data.children["winchupgradesocket"].attrs["type"].split(",")
|
||||
]
|
||||
self.DefaultWinch = list(
|
||||
{x.get_winch(data.children["winchupgradesocket"].attrs["default"]) for x in self.Winches} - {None}
|
||||
)[0]
|
||||
self.ignore_child("WinchUpgradeSocket")
|
||||
|
||||
self.ignore_attr("_noinherit")
|
||||
@ -1150,9 +1367,7 @@ class TruckTires(SnowrunnerClass):
|
||||
self.TruckTire = None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_child(data, "TruckTire")
|
||||
if self.TruckTire and not isinstance(self.TruckTire, list):
|
||||
self.TruckTire = [self.TruckTire]
|
||||
self.load_child(data, "TruckTire", always_as_list=True)
|
||||
self.loading_finished(data)
|
||||
|
||||
def update_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
@ -1161,7 +1376,7 @@ class TruckTires(SnowrunnerClass):
|
||||
|
||||
for orig, upd in zip_longest(self.TruckTire, data.children["trucktire"]):
|
||||
if orig is None:
|
||||
self.TruckTire.append(self.pak.load_class(SnowrunnerGameObjectType.TRUCK_TIRE, upd))
|
||||
self.TruckTire.append(self.pak.load_class(SnowrunnerGameObjectType.TRUCK_TIRE, upd, dlc_name=self.DLC))
|
||||
elif upd is None:
|
||||
return
|
||||
else:
|
||||
@ -1202,6 +1417,13 @@ class TruckWheels(SnowrunnerClass):
|
||||
self.WidthRear = -1.0
|
||||
self.TruckTires = None
|
||||
|
||||
def get_truck_tire(self, name: str) -> TruckTire | None:
|
||||
for x in self.TruckTires.TruckTire:
|
||||
if x.Name == name:
|
||||
return x
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_attr(data, "DamageCapacity")
|
||||
self.load_attr(data, "Mass")
|
||||
@ -1276,12 +1498,21 @@ class Wheels(SnowrunnerClass):
|
||||
def __init__(self, pak_loader: InitialPakLoader):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.Wheel = None
|
||||
self.DefaultTire = None
|
||||
self.DefaultWheelType = None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
if "defaultwheeltype" in data.attrs:
|
||||
self.DefaultWheelType = self.pak.get_object(
|
||||
SnowrunnerGameObjectType.TRUCK_WHEELS, name=data.attrs["defaultwheeltype"]
|
||||
)
|
||||
self.ignore_attr("DefaultWheelType")
|
||||
self.ignore_attr("DefaultRim")
|
||||
if "defaulttire" in data.attrs:
|
||||
self.DefaultTire = self.DefaultWheelType.get_truck_tire(data.attrs["defaulttire"])
|
||||
self.ignore_attr("DefaultTire")
|
||||
self.load_child(data, "Wheel")
|
||||
|
||||
self.ignore_attr("DefaultRim")
|
||||
self.load_child(data, "Wheel", always_as_list=True)
|
||||
self.loading_finished(data)
|
||||
|
||||
|
||||
@ -1349,8 +1580,15 @@ class WinchVariants(SnowrunnerClass):
|
||||
super().__init__(pak_loader=pak_loader)
|
||||
self.Winch = []
|
||||
|
||||
def get_winch(self, name: str) -> Winch | None:
|
||||
for e in self.Winch:
|
||||
if e.Name == name:
|
||||
return e
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, data: SnowRunnerXNLNode) -> None:
|
||||
self.load_child(data, "Winch")
|
||||
self.load_child(data, "Winch", always_as_list=True)
|
||||
self.load_child(data, "WinchParams")
|
||||
self.loading_finished(data)
|
||||
|
||||
@ -1366,6 +1604,15 @@ class LanguageLoader(dict):
|
||||
value = parts[-1].replace('"', "").replace("\\", '"')
|
||||
self[key] = value
|
||||
|
||||
def get_sanitized(self, key: str, default: str | None = None) -> str:
|
||||
if key not in self:
|
||||
if default is not None:
|
||||
return default
|
||||
else:
|
||||
raise KeyError(key)
|
||||
|
||||
return " ".join(x[0] + x[1:].lower() for x in re.split(r"[-_ ]+", self[key]))
|
||||
|
||||
|
||||
TYPE_CLASS_MATRIX = {
|
||||
"addonslots": AddonSlots,
|
||||
@ -1449,6 +1696,7 @@ class SnowrunnerGameObjectType(str, Enum):
|
||||
WINCH_PARAMS = "winchparams"
|
||||
WINCH_VARIANTS = "winchvariants"
|
||||
|
||||
@property
|
||||
def snowrunner_class(self):
|
||||
return TYPE_CLASS_MATRIX[self.value]
|
||||
|
||||
@ -1532,6 +1780,7 @@ class InitialPakLoader:
|
||||
self.pak_zip = get_initial_pak_zipfile(get_initial_pak_path())
|
||||
self.is_loaded = False
|
||||
self.languages = {}
|
||||
self.dlc_names = {x: {} for x in SnowrunnerGameObjectType}
|
||||
self.raw_data = {x: {} for x in SnowrunnerGameObjectType}
|
||||
self.game_data = {x: {} for x in SnowrunnerGameObjectType}
|
||||
self.templates = {x: {} for x in SnowrunnerGameObjectType}
|
||||
@ -1550,6 +1799,7 @@ class InitialPakLoader:
|
||||
self,
|
||||
object_type: SnowrunnerGameObjectType,
|
||||
data: SnowRunnerXNLNode,
|
||||
dlc_name: str,
|
||||
parent: str | None = None,
|
||||
lazy: bool = False,
|
||||
) -> SnowrunnerClass | list[SnowrunnerClass] | None:
|
||||
@ -1557,37 +1807,40 @@ class InitialPakLoader:
|
||||
template = self.get_object(object_type=object_type, name=parent, lazy=lazy)
|
||||
if template is None:
|
||||
return None
|
||||
ret = object_type.snowrunner_class().from_template(pak_loader=self, template=template)
|
||||
ret = object_type.snowrunner_class.from_template(pak_loader=self, template=template)
|
||||
ret.DLC = dlc_name
|
||||
ret.update_data(data)
|
||||
return ret
|
||||
|
||||
if not isinstance(data, list):
|
||||
if "_template" not in data.attrs:
|
||||
ret = object_type.snowrunner_class()(pak_loader=self)
|
||||
ret = object_type.snowrunner_class(pak_loader=self)
|
||||
else:
|
||||
ret = object_type.snowrunner_class().from_template(
|
||||
ret = object_type.snowrunner_class.from_template(
|
||||
pak_loader=self, template=self.get_template(object_type=object_type, name=data.attrs["_template"])
|
||||
)
|
||||
|
||||
ret.DLC = dlc_name
|
||||
ret.load_data(data)
|
||||
else:
|
||||
ret = []
|
||||
for sub_data in data:
|
||||
if "_template" not in sub_data.attrs:
|
||||
ret.append(object_type.snowrunner_class()(pak_loader=self))
|
||||
ret.append(object_type.snowrunner_class(pak_loader=self))
|
||||
else:
|
||||
ret.append(
|
||||
object_type.snowrunner_class().from_template(
|
||||
object_type.snowrunner_class.from_template(
|
||||
pak_loader=self,
|
||||
template=self.get_template(object_type=object_type, name=sub_data.attrs["_template"]),
|
||||
)
|
||||
)
|
||||
|
||||
ret[-1].DLC = dlc_name
|
||||
ret[-1].load_data(sub_data)
|
||||
|
||||
return ret
|
||||
|
||||
def load_templates(self, data: dict, local: bool = False) -> None:
|
||||
def load_templates(self, data: dict, dlc_name: str, local: bool = False) -> None:
|
||||
if local:
|
||||
self.templates_local = {x: {} for x in SnowrunnerGameObjectType}
|
||||
|
||||
@ -1610,18 +1863,26 @@ class InitialPakLoader:
|
||||
continue
|
||||
if local:
|
||||
self.templates_local[category][tp_name] = self.load_class(
|
||||
object_type=object_type, data=sub_data
|
||||
object_type=object_type,
|
||||
data=sub_data,
|
||||
dlc_name=dlc_name,
|
||||
)
|
||||
else:
|
||||
self.templates[category][tp_name] = self.load_class(object_type=object_type, data=sub_data)
|
||||
self.templates[category][tp_name] = self.load_class(
|
||||
object_type=object_type, data=sub_data, dlc_name=dlc_name
|
||||
)
|
||||
if defer:
|
||||
for tp_name, sub_data in defer:
|
||||
if local:
|
||||
self.templates_local[category][tp_name] = self.load_class(
|
||||
object_type=object_type, data=sub_data
|
||||
object_type=object_type,
|
||||
data=sub_data,
|
||||
dlc_name=dlc_name,
|
||||
)
|
||||
else:
|
||||
self.templates[category][tp_name] = self.load_class(object_type=object_type, data=sub_data)
|
||||
self.templates[category][tp_name] = self.load_class(
|
||||
object_type=object_type, data=sub_data, dlc_name=dlc_name
|
||||
)
|
||||
|
||||
elif category in self.ignore_categories:
|
||||
pass # Ignore for now
|
||||
@ -1657,6 +1918,9 @@ class InitialPakLoader:
|
||||
logger.error(f"Unknown Game Data Object requested: {name} in {object_type.name}")
|
||||
sys.exit(1)
|
||||
|
||||
def get_object_list(self, object_type: SnowrunnerGameObjectType) -> list:
|
||||
return list(self.game_data[object_type].values())
|
||||
|
||||
def get_sorted_object_list(
|
||||
self, object_type: SnowrunnerGameObjectType, language: LanguageLoader
|
||||
) -> list[tuple[str, SnowrunnerClass]]:
|
||||
@ -1664,12 +1928,13 @@ class InitialPakLoader:
|
||||
sorted(self.game_data[object_type].items(), key=lambda v: v[1].get_translated_ui_name(language)),
|
||||
)
|
||||
|
||||
def store_raw(self, ref: str, data: dict):
|
||||
def store_raw(self, ref: str, dlc_name: str, data: dict):
|
||||
cat_in_matrix = set(data.keys()) & set(SnowrunnerGameObjectType.values())
|
||||
cat_in_ignore = set(data.keys()) & self.ignore_categories
|
||||
|
||||
if cat_in_matrix:
|
||||
self.raw_data[SnowrunnerGameObjectType(list(cat_in_matrix)[0])][ref] = data
|
||||
self.dlc_names[SnowrunnerGameObjectType(list(cat_in_matrix)[0])][ref] = dlc_name
|
||||
elif cat_in_ignore:
|
||||
pass # not sure what to do with this, yet
|
||||
else:
|
||||
@ -1686,6 +1951,8 @@ class InitialPakLoader:
|
||||
for member in self.pak_zip.infolist():
|
||||
if not member.filename.endswith(".xml"):
|
||||
continue
|
||||
|
||||
dlc_name = member.filename.split("\\")[2] if "_dlc" in member.filename else None
|
||||
filename_ref = member.filename.split("\\")[-1].replace(".xml", "")
|
||||
logger.info(f" - {member.filename}")
|
||||
with self.pak_zip.open(member) as d_file:
|
||||
@ -1695,13 +1962,13 @@ class InitialPakLoader:
|
||||
|
||||
if len(data) == 1 and "_templates" in data:
|
||||
logger.info(f" - -> Loading templates from {member.filename}")
|
||||
self.load_templates(data["_templates"].children)
|
||||
self.load_templates(data["_templates"].children, dlc_name=dlc_name)
|
||||
continue
|
||||
elif len(data) > 3:
|
||||
logger.error(f"Multiple keys?! ({list(data.keys())}")
|
||||
sys.exit(1)
|
||||
|
||||
self.store_raw(filename_ref, data)
|
||||
self.store_raw(filename_ref, dlc_name, data)
|
||||
|
||||
deferred_loads = []
|
||||
logger.info("Loading GameData")
|
||||
@ -1715,9 +1982,13 @@ class InitialPakLoader:
|
||||
|
||||
logger.info(f" - {ref}")
|
||||
if "_templates" in data:
|
||||
self.load_templates(data["_templates"].children, local=True)
|
||||
self.load_templates(
|
||||
data["_templates"].children, local=True, dlc_name=self.dlc_names[object_type][ref]
|
||||
)
|
||||
|
||||
self.game_data[object_type][ref] = self.load_class(object_type, data[category])
|
||||
self.game_data[object_type][ref] = self.load_class(
|
||||
object_type, data[category], dlc_name=self.dlc_names[object_type][ref]
|
||||
)
|
||||
|
||||
del self.raw_data[object_type]
|
||||
|
||||
@ -1726,9 +1997,15 @@ class InitialPakLoader:
|
||||
for object_type, ref, data in deferred_loads:
|
||||
logger.info(f" - {ref}")
|
||||
if "_templates" in data:
|
||||
self.load_templates(data["_templates"].children, local=True)
|
||||
self.load_templates(
|
||||
data["_templates"].children, local=True, dlc_name=self.dlc_names[object_type][ref]
|
||||
)
|
||||
sr_class = self.load_class(
|
||||
object_type, data[category], parent=data["_parent"].get_attr("file"), lazy=True
|
||||
object_type,
|
||||
data[category],
|
||||
parent=data["_parent"].get_attr("file"),
|
||||
lazy=True,
|
||||
dlc_name=self.dlc_names[object_type][ref],
|
||||
)
|
||||
if sr_class is None:
|
||||
still_deferred_loads.append((object_type, ref, data))
|
||||
@ -1743,7 +2020,9 @@ class InitialPakLoader:
|
||||
logger.info(f" - {object_type.name}, {ref}")
|
||||
if "_templates" in data:
|
||||
self.load_templates(data["_templates"].children, local=True)
|
||||
self.game_data[object_type][ref] = self.load_class(object_type, data[category])
|
||||
self.game_data[object_type][ref] = self.load_class(
|
||||
object_type, data[category], dlc_name=self.dlc_names[object_type][ref]
|
||||
)
|
||||
deferred_loads = []
|
||||
|
||||
for cat, v in self.raw_data.items():
|
||||
|
||||
375
output.py
375
output.py
@ -1,6 +1,14 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
from loader import InitialPakLoader, LanguageLoader, SnowrunnerGameObjectType, SnowrunnerClass, GameData
|
||||
from constants import DLC_NAMES
|
||||
from loader import (
|
||||
InitialPakLoader,
|
||||
LanguageLoader,
|
||||
SnowrunnerGameObjectType,
|
||||
TruckAddonCategory,
|
||||
GameData,
|
||||
SnowrunnerClass,
|
||||
)
|
||||
from logging import getLogger
|
||||
|
||||
logger = getLogger(__name__)
|
||||
@ -13,13 +21,17 @@ def get_head(language: LanguageLoader, category: str, name: str | None = None) -
|
||||
<title>SnowRunnerDB - {category}"""
|
||||
+ (f""" - {name}""" if name is not None else "")
|
||||
+ f"""</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link href="https://cdn.datatables.net/v/dt/dt-3.0.1/datatables.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.datatables.net/v/dt/dt-3.0.1/datatables.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='navi_lang'></div>
|
||||
<div id='categories'>
|
||||
<ul id='navi_cat'>
|
||||
<li><a href="/{language.Name}/trucks/">Trucks</a></li>
|
||||
<li><a href="/{language.Name}/frame_addons/">Frame Addons</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id='content'>
|
||||
@ -44,27 +56,61 @@ def write_html(
|
||||
|
||||
|
||||
def write_trucks(pak: InitialPakLoader, destination_directory: str, language: LanguageLoader):
|
||||
index_table = ""
|
||||
|
||||
truck_types = {}
|
||||
index_content = " <h1>Trucks</h1>\n"
|
||||
index_content += f" <table id='truck_index' class='display'>\n"
|
||||
index_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Class</th>\n"
|
||||
f" <th>DLC</th>\n"
|
||||
f" <th>Unlock by</th>\n"
|
||||
f" <th>Region</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Weight</th>\n"
|
||||
f" <th>Drivetrain</th>\n"
|
||||
f" <th>AWD</th>\n"
|
||||
f" <th>DiffLock</th>\n"
|
||||
f" </tr></thead>\n"
|
||||
)
|
||||
index_content += f" <tbody>\n"
|
||||
|
||||
for reference, truck in pak.get_sorted_object_list(SnowrunnerGameObjectType.TRUCK, language):
|
||||
if truck.Type != "Truck":
|
||||
continue
|
||||
|
||||
if truck.TruckData.TruckType not in truck_types:
|
||||
truck_types[truck.TruckData.TruckType] = [(reference, truck)]
|
||||
else:
|
||||
truck_types[truck.TruckData.TruckType].append((reference, truck))
|
||||
|
||||
truck_file = os.path.join(destination_directory, reference + ".html")
|
||||
truck_content = f" <h1>{truck.get_translated_ui_name(language)}</h1>"
|
||||
truck_content += f" <table>\n"
|
||||
try:
|
||||
truck_content += f" <tr><td>DLC</td><td>{DLC_NAMES[truck.DLC]}</td></tr>\n"
|
||||
except KeyError:
|
||||
print(f"DEBUG: DLC {truck.DLC} for Truck {truck.get_translated_ui_name(language)}")
|
||||
exit(1)
|
||||
truck_content += f" <tr><td>Weight</td><td>{truck.Weight}kg</td></tr>\n"
|
||||
truck_content += f" <tr><td>AWD</td><td>{language[truck.AWDInfo.value]}</td></tr>\n"
|
||||
truck_content += f" <tr><td>DiffLock</td><td>{language[truck.DiffLockInfo.value]}</td></tr>\n"
|
||||
truck_content += f" <tr><td>Def Engine</td><td>{truck.TruckData.DefaultEngine.get_translated_ui_name(language)}</td></tr>\n"
|
||||
truck_content += f" <tr><td>Def Gearbox</td><td>{truck.TruckData.DefaultGearbox.get_translated_ui_name(language)}</td></tr>\n"
|
||||
truck_content += f" <tr><td>Def Suspension</td><td>{truck.TruckData.DefaultSuspension.get_translated_ui_name(language)}</td></tr>\n"
|
||||
truck_content += f" <tr><td>Def Winch</td><td>{truck.TruckData.DefaultWinch.get_translated_ui_name(language)}</td></tr>\n"
|
||||
truck_content += f" <tr><td>Def Tire</td><td>{truck.TruckData.Wheels.DefaultTire.get_translated_ui_name(language)}</td></tr>\n"
|
||||
truck_content += f" </table>\n"
|
||||
|
||||
# Engines
|
||||
truck_content += f" <h2>Engines</h2>\n"
|
||||
engines = [x for y in truck.TruckData.Engines for x in y.Engines]
|
||||
truck_content += f" <table>\n"
|
||||
truck_content += f" <tr><th>Name</th><th>Price</th><th>Torque</th><th>Base Fuel Consumption</th><th>Unlock By</th></tr>\n"
|
||||
engines = [x for y in truck.TruckData.Engines for x in y.Engine]
|
||||
truck_content += f" <table id='engines' data-order='[[2, \"asc\"]]'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Base Fuel Consumption</th>\n"
|
||||
f" <th>Torque</th>\n"
|
||||
f" <th>Power-to-Weight</th>\n"
|
||||
f" </tr></thead>\n"
|
||||
)
|
||||
truck_content += f" <tbody>\n"
|
||||
# Power2Weight
|
||||
# Power2Fuel
|
||||
for engine in sorted(engines, key=lambda x: x.Torque):
|
||||
@ -72,22 +118,92 @@ def write_trucks(pak: InitialPakLoader, destination_directory: str, language: La
|
||||
f" <tr>"
|
||||
f" <td>{engine.get_translated_ui_name(language)}</td>"
|
||||
f" <td>{engine.GameData.Price}</th>"
|
||||
f"<td>{engine.Torque}</td>"
|
||||
f"<td>{engine.FuelConsumption}</td>"
|
||||
f" <td>{engine.GameData.get_unlock_str()}</td>"
|
||||
f" <td>{engine.FuelConsumption}</td>"
|
||||
f" <td>{engine.Torque}</td>"
|
||||
f" <td>{engine.Torque / truck.Weight:.2f}</td>"
|
||||
f" </tr>\n"
|
||||
)
|
||||
truck_content += f" </table>\n"
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
# Gearboxes
|
||||
gearboxes = [y for x in truck.TruckData.Gearboxes for y in x.Gearbox]
|
||||
max_gear_count = max(len(x.Gear) for x in gearboxes)
|
||||
|
||||
truck_content += f" <h2>Gearboxes</h2>\n"
|
||||
truck_content += f" <h3>General</h3>\n"
|
||||
truck_content += f" <table id='gearboxes_general'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Fine-Tunable</th>\n"
|
||||
f" <th>L-</th>\n"
|
||||
f" <th>L</th>\n"
|
||||
f" <th>L+</th>\n"
|
||||
f" <th>H</th>\n"
|
||||
f" <th>Gears</th>\n"
|
||||
f" </tr></thead>\n"
|
||||
)
|
||||
truck_content += f" <tbody>\n"
|
||||
for gearbox in sorted(gearboxes, key=lambda x: x.get_translated_ui_name(language)):
|
||||
truck_content += f" <tr>\n"
|
||||
truck_content += f" <td>{gearbox.get_translated_ui_name(language)}</td>\n"
|
||||
truck_content += f" <td>{gearbox.Price}</td>\n"
|
||||
truck_content += f" <td>{gearbox.GameData.get_unlock_str()}</td>\n"
|
||||
truck_content += (
|
||||
f" <td>{'✓' if gearbox.GearboxParams.IsManualLowGear else '˙'}</td>\n"
|
||||
)
|
||||
truck_content += (
|
||||
f" <td>{'✓' if gearbox.GearboxParams.IsLowerMinusGearExists else '˙'}</td>\n"
|
||||
)
|
||||
truck_content += (
|
||||
f" <td>{'✓' if gearbox.GearboxParams.IsLowerGearExists else '˙'}</td>\n"
|
||||
)
|
||||
truck_content += (
|
||||
f" <td>{'✓' if gearbox.GearboxParams.IsLowerPlusGearExists else '˙'}</td>\n"
|
||||
)
|
||||
truck_content += (
|
||||
f" <td>{'✓' if gearbox.GearboxParams.IsHighGearExists else '˙'}</td>\n"
|
||||
)
|
||||
truck_content += f" <td>{len(gearbox.Gear)}</td>\n"
|
||||
truck_content += f" </tr>\n"
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
truck_content += f" <h3>Fuel Consumption</h3>\n"
|
||||
truck_content += f" <table id='gearboxes_fuel'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Fuel Consumption</th>\n"
|
||||
f" <th>AWD Fuel Modifier</th>\n"
|
||||
)
|
||||
for i in range(max_gear_count):
|
||||
gear_num = str(i + 1) if i > 0 else "L+/1"
|
||||
truck_content += f" <th>{gear_num}</th>\n"
|
||||
truck_content += f" </tr></thead>\n"
|
||||
truck_content += f" <tbody>\n"
|
||||
|
||||
for gearbox in sorted(gearboxes, key=lambda x: x.get_translated_ui_name(language)):
|
||||
truck_content += f" <tr>\n"
|
||||
truck_content += f" <td>{gearbox.get_translated_ui_name(language)}</td>\n"
|
||||
truck_content += f" <td>{gearbox.FuelConsumption}</td>\n"
|
||||
truck_content += f" <td>{gearbox.AWDConsumptionModifier}</td>\n"
|
||||
for i in range(max_gear_count):
|
||||
if i >= len(gearbox.Gear):
|
||||
truck_content += f" <td>-</td>\n"
|
||||
else:
|
||||
truck_content += f" <td>{gearbox.Gear[i].FuelModifier}</td>\n"
|
||||
truck_content += f" </tr>\n"
|
||||
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
# Suspensions
|
||||
truck_content += f" <h2>Suspensions</h2>\n"
|
||||
# Winches
|
||||
truck_content += f" <h2>Winches</h2>\n"
|
||||
|
||||
# Wheels
|
||||
truck_content += f" <h2>Wheels</h2>\n"
|
||||
truck_content += f" <h2>Tires</h2>\n"
|
||||
wheel_cat = {}
|
||||
scales = set()
|
||||
for cw in truck.TruckData.CompatibleWheels:
|
||||
@ -124,6 +240,124 @@ def write_trucks(pak: InitialPakLoader, destination_directory: str, language: La
|
||||
scales = ", ".join(['%d"' % x for x in sorted(scales)])
|
||||
truck_content += f" <h4>Available Tire Sizes: {scales}</h4>\n"
|
||||
|
||||
# Winches
|
||||
truck_content += f" <h2>Winches</h2>\n"
|
||||
|
||||
# Diff. Lock
|
||||
diff_lock_addons = truck.get_available_truck_addons(TruckAddonCategory.DIFF_LOCK)
|
||||
if diff_lock_addons:
|
||||
truck_content += f" <h2>Diff. Lock</h2>\n"
|
||||
truck_content += f" <table id='diff_lock'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" </tr></thead><tbody>\n"
|
||||
)
|
||||
for addon in sorted(diff_lock_addons, key=lambda v: v.get_translated_ui_name(language)):
|
||||
truck_content += (
|
||||
f" <tr>\n"
|
||||
f" <td>{addon.get_translated_ui_name(language)}</td>\n"
|
||||
f" <td>{addon.Price}</td>\n"
|
||||
f" <td>{addon.GameData.get_unlock_str()}</td>\n"
|
||||
f" </tr>\n"
|
||||
)
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
# Spare Wheel (if any)
|
||||
# Snorkel (if any)
|
||||
snorkel_addons = truck.get_available_truck_addons(TruckAddonCategory.SNORKEL)
|
||||
if snorkel_addons:
|
||||
truck_content += f" <h2>Snorkel</h2>\n"
|
||||
truck_content += f" <table id='snorkel'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Mass</th>\n"
|
||||
f" </tr></thead><tbody>\n"
|
||||
)
|
||||
for addon in sorted(snorkel_addons, key=lambda v: v.get_translated_ui_name(language)):
|
||||
truck_content += (
|
||||
f" <tr>\n"
|
||||
f" <td>{addon.get_translated_ui_name(language)}</td>\n"
|
||||
f" <td>{addon.Price}</td>\n"
|
||||
f" <td>{addon.GameData.get_unlock_str()}</td>\n"
|
||||
f" <td>{addon.PhysicsModel.Weight}kg</td>\n"
|
||||
f" </tr>\n"
|
||||
)
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
# AWD
|
||||
awd_addons = truck.get_available_truck_addons(TruckAddonCategory.ALL_WHEEL_DRIVE)
|
||||
if awd_addons:
|
||||
truck_content += f" <h2>AWD</h2>\n"
|
||||
truck_content += f" <table id='awd'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Mass</th>\n"
|
||||
f" </tr></thead><tbody>\n"
|
||||
)
|
||||
for addon in sorted(awd_addons, key=lambda v: v.get_translated_ui_name(language)):
|
||||
truck_content += (
|
||||
f" <tr>\n"
|
||||
f" <td>{addon.get_translated_ui_name(language)}</td>\n"
|
||||
f" <td>{addon.Price}</td>\n"
|
||||
f" <td>{addon.GameData.get_unlock_str()}</td>\n"
|
||||
f" <td>{addon.PhysicsModel.Weight}kg</td>\n"
|
||||
f" </tr>\n"
|
||||
)
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
# Frame Addons
|
||||
|
||||
truck_content += f" <h2>Frame Addons</h2>\n"
|
||||
truck_content += f" <table id='frame_addons'>\n"
|
||||
truck_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Mass</th>\n"
|
||||
f" <th>Cargo Slots</th>\n"
|
||||
f" <th>Repair Points</th>\n"
|
||||
f" <th>Spare Wheels</th>\n"
|
||||
f" <th>Fuel Capacity</th>\n"
|
||||
f" <th>Water Capacity</th>\n"
|
||||
f" </tr></thead><tbody>\n"
|
||||
)
|
||||
for truck_addon in sorted(
|
||||
truck.get_available_truck_addons(TruckAddonCategory.FRAME_ADDON),
|
||||
key=lambda v: v.get_translated_ui_name(language),
|
||||
):
|
||||
truck_content += f" <tr>\n"
|
||||
truck_content += f" <td>{truck_addon.get_translated_ui_name(language)}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.Price}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.GameData.get_unlock_str()}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.PhysicsModel.Weight}kg</td>\n"
|
||||
truck_content += f" <td>{truck_addon.AddonSlots.Quantity if truck_addon.AddonSlots is not None else ' '}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.TruckData.RepairsCapacity if truck_addon.TruckData is not None and truck_addon.TruckData.RepairsCapacity > 0 else ' '}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.TruckData.WheelRepairsCapacity if truck_addon.TruckData is not None and truck_addon.TruckData.WheelRepairsCapacity > 0 else ' '}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.TruckData.FuelCapacity if truck_addon.TruckData is not None and truck_addon.TruckData.FuelCapacity > 0 else ' '}</td>\n"
|
||||
truck_content += f" <td>{truck_addon.TruckData.WaterCapacity if truck_addon.TruckData is not None and truck_addon.TruckData.WaterCapacity > 0 else ' '}</td>\n"
|
||||
truck_content += f" </tr>\n"
|
||||
truck_content += f" </tbody></table>\n"
|
||||
|
||||
truck_content += f" <script>\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#engines', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#gearboxes_general', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#gearboxes_fuel', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#diff_lock', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#snorkel', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#awd', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += " document.addEventListener('DOMContentLoaded', function() {new DataTable('#frame_addons', {info: false, paging: false, searching: false});});\n"
|
||||
truck_content += f" </script>\n"
|
||||
|
||||
write_html(
|
||||
truck_file,
|
||||
language=language,
|
||||
@ -132,20 +366,99 @@ def write_trucks(pak: InitialPakLoader, destination_directory: str, language: La
|
||||
name=truck.get_translated_ui_name(language),
|
||||
)
|
||||
|
||||
index_content = " <h1>Trucks</h1>\n"
|
||||
for truck_type in sorted(truck_types):
|
||||
index_content += f" <h2>{truck_type}</h2>\n"
|
||||
index_content += f" <table>\n"
|
||||
index_content += f" <tr><th>Name</th></tr>\n"
|
||||
for reference, truck in sorted(truck_types[truck_type], key=lambda v: v[1].get_translated_ui_name(language)):
|
||||
index_content += f" <tr><td><a href='{reference}.html'>{truck.get_translated_ui_name(language)}</a></th></tr>\n"
|
||||
index_content += f" </table>\n"
|
||||
index_content += (
|
||||
f" <tr>\n"
|
||||
f" <td><a href='{reference}.html'>{truck.get_translated_ui_name(language)}</a></td>\n"
|
||||
f" <td>{language.get_sanitized(truck.TruckData.TruckType)}</td>\n"
|
||||
f" <td>{DLC_NAMES[truck.DLC]}</td>\n"
|
||||
f" <td>{truck.GameData.get_unlock_str()}</td>\n"
|
||||
f" <td>{truck.GameData.Country}</td>\n"
|
||||
f" <td>{truck.get_shop_price()}</td>\n"
|
||||
f" <td>{truck.Weight}</td>\n"
|
||||
f" <td>{truck.DriveTrain}</td>\n"
|
||||
f" <td>{language[truck.AWDInfo.value]}</td>\n"
|
||||
f" <td>{language[truck.DiffLockInfo.value]}</td>\n"
|
||||
f" </tr>\n"
|
||||
)
|
||||
|
||||
index_content += f" </tbody></table>\n"
|
||||
index_content += " <script>document.addEventListener('DOMContentLoaded', function() {new DataTable('#truck_index', {paging: false});});</script>\n"
|
||||
|
||||
write_html(
|
||||
os.path.join(destination_directory, "index.html"), language=language, content=index_content, category="Trucks"
|
||||
)
|
||||
|
||||
|
||||
def write_frame_addons(pak: InitialPakLoader, destination_directory: str, language: LanguageLoader):
|
||||
index_content = " <h1>Frame Addons</h1>\n"
|
||||
index_content += f" <table id='frame_addons' class='display'>\n"
|
||||
index_content += (
|
||||
f" <thead><tr>\n"
|
||||
f" <th>Name</th>\n"
|
||||
f" <th>Price</th>\n"
|
||||
f" <th>Unlock By</th>\n"
|
||||
f" <th>Mass</th>\n"
|
||||
f" <th>Cargo Slots</th>\n"
|
||||
f" <th>Repair Points</th>\n"
|
||||
f" <th>Spare Wheels</th>\n"
|
||||
f" <th>Fuel Capacity</th>\n"
|
||||
f" <th>Water Capacity</th>\n"
|
||||
f" <th>Available for</th>\n"
|
||||
f" </tr></thead>\n"
|
||||
)
|
||||
index_content += f" <tbody>\n"
|
||||
|
||||
for _, frame_addon in pak.get_sorted_object_list(SnowrunnerGameObjectType.TRUCK_ADDON, language=language):
|
||||
if frame_addon.GameData.Category != TruckAddonCategory.FRAME_ADDON:
|
||||
continue
|
||||
|
||||
if (
|
||||
"TRANSLATION NOT FOUND" in frame_addon.get_translated_ui_name(language)
|
||||
or len(frame_addon.get_translated_ui_name(language))
|
||||
> 100 # This is a hidden frame addon where somebody swapped UI_NAME and UI_DESC
|
||||
):
|
||||
continue # FIXME: How To better detect Hidden Frame Addons not shown in game?
|
||||
# Also e.g. Mack Defense has a Spare Wheel Addon that's always installed and not shown in game?!
|
||||
|
||||
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 not available_trucks:
|
||||
continue
|
||||
|
||||
index_content += f" <tr>\n"
|
||||
index_content += f" <td>{frame_addon.get_translated_ui_name(language)}</td>\n"
|
||||
index_content += f" <td>{frame_addon.Price}</td>\n"
|
||||
index_content += f" <td>{frame_addon.GameData.get_unlock_str()}</td>\n"
|
||||
index_content += f" <td>{frame_addon.PhysicsModel.Weight}kg</td>\n"
|
||||
index_content += f" <td>{frame_addon.AddonSlots.Quantity if frame_addon.AddonSlots is not None else ' '}</td>\n"
|
||||
index_content += f" <td>{frame_addon.TruckData.RepairsCapacity if frame_addon.TruckData is not None and frame_addon.TruckData.RepairsCapacity > 0 else ' '}</td>\n"
|
||||
index_content += f" <td>{frame_addon.TruckData.WheelRepairsCapacity if frame_addon.TruckData is not None and frame_addon.TruckData.WheelRepairsCapacity > 0 else ' '}</td>\n"
|
||||
index_content += f" <td>{frame_addon.TruckData.FuelCapacity if frame_addon.TruckData is not None and frame_addon.TruckData.FuelCapacity > 0 else ' '}</td>\n"
|
||||
index_content += f" <td>{frame_addon.TruckData.WaterCapacity if frame_addon.TruckData is not None and frame_addon.TruckData.WaterCapacity > 0 else ' '}</td>\n"
|
||||
|
||||
truck_links = ", ".join(
|
||||
f'<a href="../trucks/{ref}.html">{truck.get_translated_ui_name(language)}</a>'
|
||||
for ref, truck in available_trucks
|
||||
)
|
||||
index_content += f" <td>{truck_links}</td>\n"
|
||||
|
||||
index_content += f" </tr>\n"
|
||||
|
||||
index_content += f" </tbody>\n"
|
||||
index_content += f" </table>\n"
|
||||
index_content += " <script>document.addEventListener('DOMContentLoaded', function() {new DataTable('#frame_addons', {paging: false});});</script>\n"
|
||||
|
||||
write_html(
|
||||
os.path.join(destination_directory, "index.html"),
|
||||
language=language,
|
||||
content=index_content,
|
||||
category="Frame Addons",
|
||||
)
|
||||
|
||||
|
||||
def create_html(pak: InitialPakLoader, destination_directory: str = "output/"):
|
||||
if not os.path.exists(destination_directory):
|
||||
os.mkdir(destination_directory)
|
||||
@ -157,4 +470,12 @@ def create_html(pak: InitialPakLoader, destination_directory: str = "output/"):
|
||||
if not os.path.exists(language_path):
|
||||
os.mkdir(language_path)
|
||||
|
||||
write_trucks(pak=pak, destination_directory=language_path, language=language_data)
|
||||
truck_path = os.path.join(destination_directory, language_name, "trucks")
|
||||
if not os.path.exists(truck_path):
|
||||
os.mkdir(truck_path)
|
||||
write_trucks(pak=pak, destination_directory=truck_path, language=language_data)
|
||||
|
||||
frame_addon_path = os.path.join(destination_directory, language_name, "frame_addons")
|
||||
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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user