aboutsummaryrefslogtreecommitdiff
path: root/libs/basic_games/games/game_stalkeranomaly.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/basic_games/games/game_stalkeranomaly.py')
-rw-r--r--libs/basic_games/games/game_stalkeranomaly.py293
1 files changed, 293 insertions, 0 deletions
diff --git a/libs/basic_games/games/game_stalkeranomaly.py b/libs/basic_games/games/game_stalkeranomaly.py
new file mode 100644
index 0000000..bb40575
--- /dev/null
+++ b/libs/basic_games/games/game_stalkeranomaly.py
@@ -0,0 +1,293 @@
+from enum import IntEnum
+from pathlib import Path
+
+from PyQt6.QtCore import QDir, QFileInfo, Qt
+from PyQt6.QtWidgets import QLabel, QVBoxLayout, QWidget
+
+import mobase
+
+from ..basic_features.basic_save_game_info import (
+ BasicGameSaveGame,
+ BasicGameSaveGameInfo,
+)
+from ..basic_game import BasicGame
+from .stalkeranomaly import XRSave
+
+
+class StalkerAnomalyModDataChecker(mobase.ModDataChecker):
+ _valid_folders: list[str] = [
+ "appdata",
+ "bin",
+ "db",
+ "gamedata",
+ ]
+
+ def hasValidFolders(self, tree: mobase.IFileTree) -> bool:
+ for e in tree:
+ if e.isDir():
+ if e.name().lower() in self._valid_folders:
+ return True
+
+ return False
+
+ def findLostData(self, tree: mobase.IFileTree) -> list[mobase.FileTreeEntry]:
+ lost_db: list[mobase.FileTreeEntry] = []
+
+ for e in tree:
+ if e.isFile():
+ if e.suffix().lower().startswith("db"):
+ lost_db.append(e)
+
+ return lost_db
+
+ def dataLooksValid(
+ self, filetree: mobase.IFileTree
+ ) -> mobase.ModDataChecker.CheckReturn:
+ if self.hasValidFolders(filetree):
+ return mobase.ModDataChecker.VALID
+
+ if self.findLostData(filetree):
+ return mobase.ModDataChecker.FIXABLE
+
+ return mobase.ModDataChecker.INVALID
+
+ def fix(self, filetree: mobase.IFileTree) -> mobase.IFileTree:
+ lost_db = self.findLostData(filetree)
+ if lost_db:
+ rfolder = filetree.addDirectory("db").addDirectory("mods")
+ for r in lost_db:
+ rfolder.insert(r, mobase.IFileTree.REPLACE)
+
+ return filetree
+
+
+class Content(IntEnum):
+ INTERFACE = 0
+ TEXTURE = 1
+ MESH = 2
+ SCRIPT = 3
+ SOUND = 4
+ MCM = 5
+ CONFIG = 6
+
+
+class StalkerAnomalyModDataContent(mobase.ModDataContent):
+ content: list[int] = []
+
+ def getAllContents(self) -> list[mobase.ModDataContent.Content]:
+ return [
+ mobase.ModDataContent.Content(
+ Content.INTERFACE, "Interface", ":/MO/gui/content/interface"
+ ),
+ mobase.ModDataContent.Content(
+ Content.TEXTURE, "Textures", ":/MO/gui/content/texture"
+ ),
+ mobase.ModDataContent.Content(
+ Content.MESH, "Meshes", ":/MO/gui/content/mesh"
+ ),
+ mobase.ModDataContent.Content(
+ Content.SCRIPT, "Scripts", ":/MO/gui/content/script"
+ ),
+ mobase.ModDataContent.Content(
+ Content.SOUND, "Sounds", ":/MO/gui/content/sound"
+ ),
+ mobase.ModDataContent.Content(Content.MCM, "MCM", ":/MO/gui/content/menu"),
+ mobase.ModDataContent.Content(
+ Content.CONFIG, "Configs", ":/MO/gui/content/inifile"
+ ),
+ ]
+
+ def walkContent(
+ self, path: str, entry: mobase.FileTreeEntry
+ ) -> mobase.IFileTree.WalkReturn:
+ name = entry.name().lower()
+ if entry.isFile():
+ ext = entry.suffix().lower()
+ if ext in ["dds", "thm"]:
+ self.content.append(Content.TEXTURE)
+ if path.startswith("gamedata/textures/ui"):
+ self.content.append(Content.INTERFACE)
+ elif ext in ["omf", "ogf"]:
+ self.content.append(Content.MESH)
+ elif ext in ["script"]:
+ self.content.append(Content.SCRIPT)
+ if "_mcm" in name:
+ self.content.append(Content.MCM)
+ elif ext in ["ogg"]:
+ self.content.append(Content.SOUND)
+ elif ext in ["ltx", "xml"]:
+ self.content.append(Content.CONFIG)
+ if path.startswith("gamedata/configs/ui"):
+ self.content.append(Content.INTERFACE)
+
+ return mobase.IFileTree.WalkReturn.CONTINUE
+
+ def getContentsFor(self, filetree: mobase.IFileTree) -> list[int]:
+ self.content = []
+ filetree.walk(self.walkContent, "/")
+ return self.content
+
+
+class StalkerAnomalySaveGame(BasicGameSaveGame):
+ _filepath: Path
+
+ xr_save: XRSave
+
+ def __init__(self, filepath: Path):
+ super().__init__(filepath)
+ self._filepath = filepath
+ self.xr_save = XRSave(self._filepath)
+
+ def getName(self) -> str:
+ xr_save = self.xr_save
+ player = xr_save.player
+ if player:
+ name = player.character_name_str
+ time = xr_save.time_fmt
+ return f"{name}, {xr_save.save_fmt} [{time}]"
+ return ""
+
+ def allFiles(self) -> list[str]:
+ filepath = str(self._filepath)
+ paths = [filepath]
+ scoc = filepath.replace(".scop", ".scoc")
+ if Path(scoc).exists():
+ paths.append(scoc)
+ dds = filepath.replace(".scop", ".dds")
+ if Path(dds).exists():
+ paths.append(dds)
+ return paths
+
+
+class StalkerAnomalySaveGameInfoWidget(mobase.ISaveGameInfoWidget):
+ def __init__(self, parent: QWidget | None):
+ super().__init__(parent)
+ layout = QVBoxLayout()
+ self._labelSave = self.newLabel(layout)
+ self._labelName = self.newLabel(layout)
+ self._labelFaction = self.newLabel(layout)
+ self._labelHealth = self.newLabel(layout)
+ self._labelMoney = self.newLabel(layout)
+ self._labelRank = self.newLabel(layout)
+ self._labelRep = self.newLabel(layout)
+ self.setLayout(layout)
+ palette = self.palette()
+ palette.setColor(self.backgroundRole(), Qt.GlobalColor.black)
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+ self.setWindowFlags(
+ Qt.WindowType.ToolTip | Qt.WindowType.BypassGraphicsProxyWidget
+ )
+
+ def newLabel(self, layout: QVBoxLayout) -> QLabel:
+ label = QLabel()
+ label.setAlignment(Qt.AlignmentFlag.AlignLeft)
+ palette = label.palette()
+ palette.setColor(label.foregroundRole(), Qt.GlobalColor.white)
+ label.setPalette(palette)
+ layout.addWidget(label)
+ layout.addStretch()
+ return label
+
+ def setSave(self, save: mobase.ISaveGame):
+ self.resize(240, 32)
+ if not isinstance(save, StalkerAnomalySaveGame):
+ return
+ xr_save = save.xr_save
+ player = xr_save.player
+ if player:
+ self._labelSave.setText(f"Save: {xr_save.save_fmt}")
+ self._labelName.setText(f"Name: {player.character_name_str}")
+ self._labelFaction.setText(f"Faction: {xr_save.getFaction()}")
+ self._labelHealth.setText(f"Health: {player.health:.2f}%")
+ self._labelMoney.setText(f"Money: {player.money} RU")
+ self._labelRank.setText(f"Rank: {xr_save.getRank()} ({player.rank})")
+ self._labelRep.setText(
+ f"Reputation: {xr_save.getReputation()} ({player.reputation})"
+ )
+
+
+class StalkerAnomalySaveGameInfo(BasicGameSaveGameInfo):
+ def getSaveGameWidget(self, parent: QWidget | None = None):
+ return StalkerAnomalySaveGameInfoWidget(parent)
+
+
+class StalkerAnomalyGame(BasicGame, mobase.IPluginFileMapper):
+ Name = "STALKER Anomaly"
+ Author = "Qudix"
+ Version = "0.5.0"
+ Description = "Adds support for STALKER Anomaly"
+
+ GameName = "STALKER Anomaly"
+ GameShortName = "stalkeranomaly"
+ GameNexusName = "stalkeranomaly"
+ GameNexusId = 3743
+ GameBinary = "AnomalyLauncher.exe"
+ GameDataPath = ""
+ GameDocumentsDirectory = "%GAME_PATH%/appdata"
+ GameSupportURL = (
+ r"https://github.com/ModOrganizer2/modorganizer-basic_games/wiki/"
+ "Game:-S.T.A.L.K.E.R.-Anomaly"
+ )
+
+ GameSaveExtension = "scop"
+ GameSavesDirectory = "%GAME_DOCUMENTS%/savedgames"
+
+ def __init__(self):
+ BasicGame.__init__(self)
+ mobase.IPluginFileMapper.__init__(self)
+
+ def init(self, organizer: mobase.IOrganizer):
+ BasicGame.init(self, organizer)
+ self._register_feature(StalkerAnomalyModDataChecker())
+ self._register_feature(StalkerAnomalyModDataContent())
+ self._register_feature(StalkerAnomalySaveGameInfo())
+ organizer.onAboutToRun(lambda _str: self.aboutToRun(_str))
+ return True
+
+ def aboutToRun(self, _str: str) -> bool:
+ gamedir = self.gameDirectory()
+ if gamedir.exists():
+ # For mappings
+ gamedir.mkdir("appdata")
+ # The game will crash if this file exists in the
+ # virtual tree rather than the game dir
+ dbg_path = Path(self._gamePath, "gamedata/configs/cache_dbg.ltx")
+ if not dbg_path.exists():
+ dbg_path.parent.mkdir(parents=True, exist_ok=True)
+ with open(dbg_path, "w", encoding="utf-8"):
+ pass
+ return True
+
+ def executables(self) -> list[mobase.ExecutableInfo]:
+ info = [
+ ["Anomaly Launcher", "AnomalyLauncher.exe"],
+ ["Anomaly (DX11-AVX)", "bin/AnomalyDX11AVX.exe"],
+ ["Anomaly (DX11)", "bin/AnomalyDX11.exe"],
+ ["Anomaly (DX10-AVX)", "bin/AnomalyDX10AVX.exe"],
+ ["Anomaly (DX10)", "bin/AnomalyDX10.exe"],
+ ["Anomaly (DX9-AVX)", "bin/AnomalyDX9AVX.exe"],
+ ["Anomaly (DX9)", "bin/AnomalyDX9.exe"],
+ ["Anomaly (DX8-AVX)", "bin/AnomalyDX8AVX.exe"],
+ ["Anomaly (DX8)", "bin/AnomalyDX8.exe"],
+ ]
+ gamedir = self.gameDirectory()
+ return [
+ mobase.ExecutableInfo(inf[0], QFileInfo(gamedir, inf[1])) for inf in info
+ ]
+
+ def listSaves(self, folder: QDir) -> list[mobase.ISaveGame]:
+ ext = self._mappings.savegameExtension.get()
+ return [
+ StalkerAnomalySaveGame(path)
+ for path in Path(folder.absolutePath()).glob(f"*.{ext}")
+ ]
+
+ def mappings(self) -> list[mobase.Mapping]:
+ appdata = self.gameDirectory().filePath("appdata")
+ m = mobase.Mapping()
+ m.createTarget = True
+ m.isDirectory = True
+ m.source = appdata
+ m.destination = appdata
+ return [m]