aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/games
diff options
context:
space:
mode:
Diffstat (limited to 'libs/basic_games/games')
-rw-r--r--libs/basic_games/games/game_kingdomcomedeliverance2.py104
-rw-r--r--libs/basic_games/games/game_sts2.py81
2 files changed, 185 insertions, 0 deletions
diff --git a/libs/basic_games/games/game_kingdomcomedeliverance2.py b/libs/basic_games/games/game_kingdomcomedeliverance2.py
new file mode 100644
index 0000000..cef7e23
--- /dev/null
+++ b/libs/basic_games/games/game_kingdomcomedeliverance2.py
@@ -0,0 +1,104 @@
+import os
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+from PyQt6.QtCore import QDir, qInfo, qWarning
+
+import mobase
+
+from ..basic_features import BasicModDataChecker, GlobPatterns
+from ..basic_game import BasicGame
+
+
+class KingdomComeDeliverance2Game(BasicGame):
+ Name = "Kingdom Come: Deliverance 2 Support Plugin"
+ Author = "TheForgotten69"
+ Version = "1.0.0"
+
+ GameName = "Kingdom Come: Deliverance II"
+ GameShortName = "kingdomcomedeliverance2"
+ GameNexusName = "kingdomcomedeliverance2"
+ GameNexusId = 7286
+ GameSteamId = [1771300]
+ GameBinary = "bin/Win64MasterMasterSteamPGO/KingdomCome.exe"
+ GameDataPath = "Mods"
+ GameSaveExtension = "whs"
+ GameDocumentsDirectory = "%GAME_PATH%"
+ GameSavesDirectory = "%USERPROFILE%/Saved Games/kingdomcome2/saves"
+
+ def init(self, organizer: mobase.IOrganizer) -> bool:
+ super().init(organizer)
+ self._register_feature(BasicModDataChecker(GlobPatterns(valid=["*"])))
+ organizer.onAboutToRun(self._write_mod_order)
+ return True
+
+ @staticmethod
+ def _get_mod_id(mod_path: Path) -> str | None:
+ """Return the mod ID for a KCD2 mod.
+
+ Checks mod.manifest for <modid> (preferred) or <name>, falling back
+ to the game mod folder name (the subfolder inside the MO2 mod directory).
+ """
+ for manifest in mod_path.rglob("mod.manifest"):
+ try:
+ root = ET.parse(manifest).getroot()
+ for tag in ("modid", "name"):
+ elem = root.find(f".//{tag}")
+ if elem is not None and elem.text and elem.text.strip():
+ return elem.text.strip()
+ except ET.ParseError:
+ qWarning(f"KCD2: failed to parse {manifest}")
+ # Fall back to the folder that contains mod.manifest
+ return manifest.parent.name
+ # No manifest — use first subdirectory name (the game mod folder)
+ for subdir in mod_path.iterdir():
+ if subdir.is_dir():
+ return subdir.name
+ return None
+
+ def _write_mod_order(self, app_path: str, wd: QDir, args: str) -> bool:
+ if not self.isActive():
+ return True
+
+ modlist = self._organizer.modList()
+ mods_path = Path(self._organizer.modsPath())
+ mod_order_path = Path(self._organizer.overwritePath()) / "mod_order.txt"
+
+ mod_ids: list[str] = []
+ for mod_name in modlist.allModsByProfilePriority():
+ if not (modlist.state(mod_name) & mobase.ModState.ACTIVE):
+ continue
+ mod_id = self._get_mod_id(mods_path / mod_name)
+ if mod_id:
+ qInfo(f"KCD2: mod '{mod_name}' -> id '{mod_id}'")
+ mod_ids.append(mod_id)
+ else:
+ qWarning(f"KCD2: could not resolve id for mod '{mod_name}', skipping")
+
+ if not mod_ids:
+ mod_order_path.unlink(missing_ok=True)
+ return True
+
+ # MO2 priority 1 = top = highest priority. KCD2 is last-loaded-wins,
+ # so highest priority must be last in the file.
+ mod_ids.reverse()
+ mod_order_path.parent.mkdir(parents=True, exist_ok=True)
+ mod_order_path.write_text("\n".join(mod_ids))
+ qInfo(f"KCD2: wrote mod_order.txt with {len(mod_ids)} mods: {mod_ids}")
+ return True
+
+ def iniFiles(self):
+ return ["custom.cfg", "system.cfg", "user.cfg"]
+
+ def initializeProfile(self, directory: QDir, settings: mobase.ProfileSetting):
+ for iniFile in self.iniFiles():
+ iniPath = self.documentsDirectory().absoluteFilePath(iniFile)
+ if not os.path.exists(iniPath):
+ with open(iniPath, "w") as _:
+ pass
+
+ modsPath = self.dataDirectory().absolutePath()
+ if not os.path.exists(modsPath):
+ os.mkdir(modsPath)
+
+ super().initializeProfile(directory, settings)
diff --git a/libs/basic_games/games/game_sts2.py b/libs/basic_games/games/game_sts2.py
new file mode 100644
index 0000000..fe992e8
--- /dev/null
+++ b/libs/basic_games/games/game_sts2.py
@@ -0,0 +1,81 @@
+from pathlib import Path
+
+from PyQt6.QtCore import QDir, QFileInfo
+
+import mobase
+
+from ..basic_features import BasicModDataChecker, GlobPatterns
+from ..basic_features.basic_save_game_info import BasicGameSaveGame
+from ..basic_game import BasicGame
+
+
+class SlayTheSpire2ModDataChecker(BasicModDataChecker):
+ def __init__(self):
+ super().__init__(
+ GlobPatterns(
+ valid=[
+ "*.pck",
+ "*.dll",
+ "*.json",
+ ],
+ move={
+ "*/*.pck": "",
+ "*/*.dll": "",
+ "*/*.json": "",
+ },
+ )
+ )
+
+
+class SlayTheSpire2Game(BasicGame):
+ Name = "Slay the Spire 2 Support Plugin"
+ Author = "Azlle"
+ Version = "1.0.1"
+
+ GameName = "Slay the Spire 2"
+ GameShortName = "slaythespire2"
+ GameNexusName = "slaythespire2"
+ GameNexusId = 8916
+ GameSteamId = 2868840
+ GameBinary = "SlayTheSpire2.exe"
+ GameDataPath = "mods"
+ GameDocumentsDirectory = "%USERPROFILE%/AppData/Roaming/SlayTheSpire2"
+
+ def init(self, organizer: mobase.IOrganizer) -> bool:
+ super().init(organizer)
+ self._register_feature(SlayTheSpire2ModDataChecker())
+ return True
+
+ def initializeProfile(self, directory: QDir, settings: mobase.ProfileSetting):
+ mods_path = Path(self.dataDirectory().absolutePath())
+ mods_path.mkdir(exist_ok=True)
+ super().initializeProfile(directory, settings)
+
+ def savesDirectory(self) -> QDir:
+ docs = QDir(self.documentsDirectory())
+ steam_dir = Path(docs.absoluteFilePath("steam"))
+ if steam_dir.exists():
+ entries = [e for e in steam_dir.iterdir() if e.is_dir()]
+ if entries:
+ return QDir(str(entries[0]))
+ return QDir(str(steam_dir))
+
+ def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]:
+ base = Path(folder.absolutePath())
+ return [
+ BasicGameSaveGame(save)
+ for save in base.rglob("*")
+ if save.is_file() and save.suffix in (".save", ".run")
+ ]
+
+ def executables(self):
+ return [
+ mobase.ExecutableInfo(
+ "Slay the Spire 2",
+ QFileInfo(self.gameDirectory().absoluteFilePath(self.binaryName())),
+ ),
+ mobase.ExecutableInfo(
+ "Slay the Spire 2 (OpenGL)",
+ QFileInfo(self.gameDirectory().absoluteFilePath(self.binaryName())),
+ ).withArgument("--rendering-driver opengl3"),
+ ]