def _GetKeyText(self, setting: UISettingsShared.SettingStandardWrapper, mod: Mods.Mod, value: bool) -> localization.LocalizedString: keyTemplate = Language.String(This.Mod.Namespace + ".Mod_Settings.Values." + setting.Key + ".Key_Template") # type: Language.String return keyTemplate.GetLocalizationString(mod.Name, mod.Author)
class SettingsList(UISettingsList.SettingsList): TitleStandard = Language.String( This.Mod.Namespace + ".Sim_Settings.List.Title") # type: Language.String def _GetTitleText(self, listPath: str) -> localization.LocalizedString: return self._GetTitleStandardText() def _GetTitleStandardText(self) -> localization.LocalizedString: return self.TitleStandard.GetLocalizationString() def _GetTitleListPathText(self, listPath: str) -> localization.LocalizedString: listPathIdentifier = listPath.replace(self.ListPathSeparator, "_") # type: str fallbackText = "List.Paths." + listPathIdentifier + ".Title" # type: str return Language.GetLocalizationStringByIdentifier( This.Mod.Namespace + ".Sim_Settings.List.Paths." + listPathIdentifier + ".Title", fallbackText=fallbackText) def _GetDescriptionText(self, listPath: str) -> localization.LocalizedString: listPathIdentifier = listPath.replace(self.ListPathSeparator, "_") # type: str return Language.GetLocalizationStringByIdentifier( This.Mod.Namespace + ".Sim_Settings.List.Paths." + listPathIdentifier + ".Description")
def _GetValueText(self, setting: UISettingsShared.SettingStandardWrapper, mod: Mods.Mod, value: bool) -> localization.LocalizedString: valueTemplate = Language.String( This.Mod.Namespace + ".Mod_Settings.Values." + setting.Key + ".Value_Template") # type: Language.String return valueTemplate.GetLocalizationString( Language.GetLocalizationStringByIdentifier( This.Mod.Namespace + ".Settings.Types.Boolean.Enabled_Disabled." + str(value), fallbackText=str(value)), str(mod.Distribution.UpdatesFileURL))
class ChoiceDialogButton(DialogButton): ChoiceButton = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Choice_Button", fallbackText="Choice_Button") # type: Language.String def __init__(self, selected: bool = False, *args, **kwargs): """ :param selected: Whether or not the button's text will have a selected look. :type selected: bool """ super().__init__(*args, **kwargs) self.Selected = selected # type: bool def GenerateDialogResponse(self) -> ui_dialog.UiDialogResponse: if self.Selected: valueButtonStringTokens = ("> ", self.Text, " <") else: valueButtonStringTokens = ("", self.Text, "") if self.ChoiceButton.IdentifierIsRegistered(): buttonTextString = self.ChoiceButton.GetCallableLocalizationString( *valueButtonStringTokens) else: buttonTextString = self.Text responseArguments = { "dialog_response_id": self.ResponseID, "sort_order": self.SortOrder, "text": buttonTextString } if self.SubText is not None: responseArguments["subtext"] = self.SubText response = ui_dialog.UiDialogResponse(**responseArguments) return response
from __future__ import annotations import abc import traceback import typing import services from NeonOcean.S4.Main import Debug, Language, This from NeonOcean.S4.Main.Tools import Exceptions, TextBuilder from NeonOcean.S4.Main.UI import Dialogs, Notifications, SettingsShared as UISettingsShared from sims4 import collections, localization from ui import ui_dialog, ui_dialog_generic, ui_dialog_notification, ui_dialog_picker, ui_text_input InvalidInputNotificationTitle = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Invalid_Input_Notification.Title" ) # type: Language.String InvalidInputNotificationText = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Invalid_Input_Notification.Text" ) # type: Language.String PresetConfirmDialogTitle = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Title") # type: Language.String PresetConfirmDialogText = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Text") # type: Language.String PresetConfirmDialogYesButton = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.Yes_Button", fallbackText="Preset_Confirm_Dialog.Yes_Button") # type: Language.String PresetConfirmDialogNoButton = Language.String( This.Mod.Namespace + ".Setting_Dialogs.Preset_Confirm_Dialog.No_Button",
def _ShowInvalidLanguageNotification() -> None: notificationArguments = { "title": InvalidLanguageNotificationTitle.GetCallableLocalizationString(), "text": InvalidLanguageNotificationText.GetCallableLocalizationString(), "expand_behavior": ui_dialog_notification.UiDialogNotification. UiDialogNotificationExpandBehavior.FORCE_EXPAND, "urgency": ui_dialog_notification.UiDialogNotification. UiDialogNotificationUrgency.URGENT } Notifications.ShowNotification(**notificationArguments) NoLocalizationPackagePacks = {Sims4Common.Pack.FP01 } # type: typing.Set[Sims4Common.Pack] InvalidLanguageNotificationTitle = MainLanguage.String( This.Mod.Namespace + ".Invalid_Language_Notification.Title") # type: MainLanguage.String InvalidLanguageNotificationText = MainLanguage.String( This.Mod.Namespace + ".Invalid_Language_Notification.Text") # type: MainLanguage.String _registeredLanguageHandlers = list( ) # type: typing.List[typing.Type[LanguageHandlerBase]]
import time import typing from NeonOcean.S4.Main import Debug, Director, Language, Paths, LoadingShared, Reporting from NeonOcean.S4.Main.Tools import Exceptions, Patcher, Python, Timer, Version from NeonOcean.S4.Main.UI import Notifications from NeonOcean.S4.Refer import GenderedLanguage, LanguageHandlers, This from NeonOcean.S4.Refer.Tools import Package, STBL from protocolbuffers import Localization_pb2 from server import client import paths as Sims4Paths from sims4 import common as Sims4Common, localization, resources from ui import ui_dialog_notification UnsupportedLanguageNotificationTitle = Language.String( This.Mod.Namespace + ".Warnings.Unsupported_Language.Title") # type: Language.String UnsupportedLanguageNotificationText = Language.String( This.Mod.Namespace + ".Warnings.Unsupported_Language.Text") # type: Language.String GameSTBLPackageMissingNotificationTitle = Language.String( This.Mod.Namespace + ".Warnings.Game_STBL_Package_Missing.Title") # type: Language.String GameSTBLPackageMissingNotificationText = Language.String( This.Mod.Namespace + ".Warnings.Game_STBL_Package_Missing.Text") # type: Language.String GameSTBLPackageReadErrorNotificationTitle = Language.String( This.Mod.Namespace + ".Warnings.Game_STBL_Package_Read_Error.Title") # type: Language.String
from __future__ import annotations import typing import webbrowser from NeonOcean.S4.Main import Debug, Language, Mods, This from NeonOcean.S4.Main.Tools import Exceptions from NeonOcean.S4.Main.UI import Dialogs from sims4 import localization from ui import ui_dialog OpenBrowserDialogText = Language.String(This.Mod.Namespace + ".Generic_Dialogs.Open_Browser_Dialog.Text") # type: Language.String OpenBrowserDialogYesButton = Language.String(This.Mod.Namespace + ".Generic_Dialogs.Open_Browser_Dialog.Yes_Button", fallbackText = "Yes_Button") # type: Language.String OpenBrowserDialogNoButton = Language.String(This.Mod.Namespace + ".Generic_Dialogs.Open_Browser_Dialog.No_Button", fallbackText = "No_Button") # type: Language.String AboutModDialogTitle = Language.String(This.Mod.Namespace + ".Generic_Dialogs.About_Mod_Dialog.Title") # type: Language.String AboutModDialogText = Language.String(This.Mod.Namespace + ".Generic_Dialogs.About_Mod_Dialog.Text") # type: Language.String AboutModDialogOkButton = Language.String(This.Mod.Namespace + ".Generic_Dialogs.About_Mod_Dialog.Ok_Button", fallbackText = "Ok_Button") # type: Language.String AboutModDialogUnknown = Language.String(This.Mod.Namespace + ".Generic_Dialogs.About_Mod_Dialog.Unknown", fallbackText = "About_Mod_Dialog.Unknown") # type: Language.String def ShowOpenBrowserDialog (url: str, returnCallback: typing.Optional[typing.Callable[[], None]] = None) -> None: """ Show a dialog asking the user to confirm their intention to open a website in their browser. :param url: The url to be opened when the user clicks the ok button. :type url: str :param returnCallback: Called after the dialog has closed. :type returnCallback: typing.Optional[typing.Callable[[], None]] """ if not isinstance(url, str): raise Exceptions.IncorrectTypeException(url, "url", (str,))
from __future__ import annotations import interactions from NeonOcean.S4.Main import Language, Mods, This from event_testing import results, test_base from sims4.tuning import tunable DisabledTooltip = Language.String( This.Mod.Namespace + ".Interactions.Support.Dependent.Disabled_Tooltip", fallbackText="Disabled_Tooltip") class DependentTest(test_base.BaseTest): # noinspection SpellCheckingInspection def __call__(self, affordance): if affordance is None: return results.TestResult(False) if not issubclass(affordance, DependentExtension): return results.TestResult(True) if affordance.DependentOnMod: if affordance.DependentMod is not None: if not affordance.DependentMod.IsLoaded(): return results.TestResult( False, tooltip=DisabledTooltip.GetCallableLocalizationString( affordance.DependentMod.Namespace)) return results.TestResult(True)
from __future__ import annotations import typing import zone from NeonOcean.S4.Main import Director, Language, This from NeonOcean.S4.Main.Saving import Save, SaveShared from NeonOcean.S4.Main.UI import Notifications SavingFirstTimeNotificationTitle = Language.String( This.Mod.Namespace + ".Saving.Saving_First_Time_Notifications.Title") # type: Language.String SavingFirstTimeNotificationText = Language.String( This.Mod.Namespace + ".Saving.Saving_First_Time_Notifications.Text") # type: Language.String _savingObject: SaveShared.Save class _Announcer(Director.Announcer): Host = This.Mod @classmethod def ZoneLoad(cls, zoneReference: zone.Zone) -> None: if not GetSavingObject().LoadedFileExisted: _ShowSavingFirstTimeNotification() def GetSavingObject() -> SaveShared.Save: return _savingObject
class CheckForUpdatesSetting(SettingsBase.Setting): Type = dict DefaultSetting: typing.Type[SettingsBase.Setting] AllModsEnabledText = Language.String(This.Mod.Namespace + ".Settings.Types.Check_For_Updates.All_Mods_Enabled", fallbackText = "Check_For_Updates.All_Mods_Enabled") # type: Language.String NoModsEnabledText = Language.String(This.Mod.Namespace + ".Settings.Types.Check_For_Updates.No_Mods_Enabled", fallbackText = "Check_For_Updates.No_Mods_Enabled") # type: Language.String CustomizedText = Language.String(This.Mod.Namespace + ".Settings.Types.Check_For_Updates.Customized", fallbackText = "Check_For_Updates.Customized") # type: Language.String @classmethod def Verify (cls, value: dict, lastChangeVersion: Version.Version = None) -> dict: if not isinstance(value, dict): raise Exceptions.IncorrectTypeException(value, "value", (dict,)) if not isinstance(lastChangeVersion, Version.Version) and lastChangeVersion is not None: raise Exceptions.IncorrectTypeException(lastChangeVersion, "lastChangeVersion", (Version.Version, "None")) for valueKey, valueValue in value.items(): # type: str, bool if not isinstance(valueKey, str): raise Exceptions.IncorrectTypeException(valueKey, "value<Key>", (str,)) if not isinstance(valueValue, bool): raise Exceptions.IncorrectTypeException(valueValue, "value[%s]" % valueKey, (str,)) return value @classmethod def GetValueText (cls, value: typing.Any) -> localization.LocalizedString: if not isinstance(value, dict): raise Exceptions.IncorrectTypeException(value, "value", (dict,)) for valueKey, valueValue in value.items(): # type: str, bool if not isinstance(valueKey, str): raise Exceptions.IncorrectTypeException(valueKey, "value<Key>", (str,)) if not isinstance(valueValue, bool): raise Exceptions.IncorrectTypeException(valueValue, "value[%s]" % valueKey, (str,)) value = cls.Get() # type: dict defaultSettingValue = cls.DefaultSetting.Get() # type: bool if len(value) == 0: if defaultSettingValue: return cls.AllModsEnabledText.GetLocalizationString() else: return cls.NoModsEnabledText.GetLocalizationString() allModsEnabled = True # type: bool noModsEnabled = True # type: bool for settingValue in value.values(): # type: str if settingValue is False: allModsEnabled = False elif settingValue is True: noModsEnabled = False if allModsEnabled and noModsEnabled: noModsEnabled = False if allModsEnabled: return cls.AllModsEnabledText.GetLocalizationString() elif noModsEnabled: return cls.NoModsEnabledText.GetLocalizationString() else: return cls.CustomizedText.GetLocalizationString() @classmethod def _OnLoad (cls) -> None: currentValue = cls.Get() # type: typing.Dict[str, bool] defaultValue = cls.DefaultSetting.Get() # type: bool currentValueChanged = False # type: bool for mod in Mods.GetAllMods(): # type: Mods.Mod if mod.Distribution.UpdatesController is None: continue if mod.Namespace not in currentValue: currentValue[mod.Namespace] = defaultValue currentValueChanged = True if currentValueChanged: cls.Set(currentValue)
import indexed_manager import services import game_services import typing import random import zone from NeonOcean.S4.Cycle import Settings, This, Guides as CycleGuides, GuideGroups as CycleGuideGroups, Reproduction, ReproductionShared from NeonOcean.S4.Cycle.Females import Shared as FemalesShared from NeonOcean.S4.Cycle.Settings import Base as ModSettingsBase from NeonOcean.S4.Cycle.SimSettings import Types as SettingsTypes from NeonOcean.S4.Main import Debug, Director, Language from NeonOcean.S4.Main.Tools import Events, Exceptions from sims import sim_info, sim_info_manager, sim_info_types _AllSimsExperiencePMSOverrideIdentifier = "AllSimsExperiencePMS" # type: str _AllSimsExperiencePMSOverrideReason = Language.String(This.Mod.Namespace + ".Sim_Settings.Values.Experiences_PMS.All_Sims_Experience_PMS_Override_Reason", fallbackText = "All_Sims_Experience_PMS_Override_Reason") _experiencesPMSRollSeed = 519490138 # type: int class ExperiencesPMS(SettingsTypes.BooleanYesNoDialogSetting): IsSetting = True # type: bool Key = "Experiences_PMS" # type: str Default = True # type: dict ListPath = "Root/Menstruation" # type: str @classmethod def IsHidden(cls, simID: str) -> bool: """ Get whether or not this setting should be visible to the user.
from __future__ import annotations import os import pathlib import typing import services from NeonOcean.S4.Main import Debug, Language, Paths, This from NeonOcean.S4.Main.Saving import Save from NeonOcean.S4.Main.UI import Dialogs from ui import ui_dialog, ui_dialog_picker SelectSaveDialogTitle = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Title") # type: Language.String SelectSaveDialogText = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Text") # type: Language.String SelectSaveDialogDescriptionCurrentlyLoaded = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Currently_Loaded", fallbackText = "Description_Currently_Loaded") # type: Language.String SelectSaveDialogDescriptionNormal = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Normal", fallbackText = "Description_Normal") # type: Language.String SelectSaveDialogDescriptionMatchMatches = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Match.Matches", fallbackText = "Description_Match.Matches") # type: Language.String SelectSaveDialogDescriptionMatchMismatchGUID = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Match.Mismatch_GUID", fallbackText = "Description_Match.Mismatch_GUID") # type: Language.String SelectSaveDialogDescriptionMatchMismatchGameTick = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Match.Mismatch_Game_Tick", fallbackText = "Description_Match.Mismatch_Game_Tick") # type: Language.String SelectSaveDialogDescriptionMatchUnknown = Language.String(This.Mod.Namespace + ".Saving.Select_Save_Dialog.Description_Match.Unknown", fallbackText = "Description_Match.Unknown") # type: Language.String def ShowSelectSaveDialog () -> None: gameSaveSlotID = str(services.get_persistence_service().get_save_slot_proto_buff().slot_id) # type: str gameSaveGUID = str(services.get_persistence_service().get_save_slot_proto_guid()) # type: str dialogArguments = { "owner": services.get_active_sim().sim_info, "title": SelectSaveDialogTitle, "text": SelectSaveDialogText.GetCallableLocalizationString(*(gameSaveSlotID, gameSaveGUID)) }
class Logger: WriteFailureNotificationTitle = Language.String(This.Mod.Namespace + ".Write_Failure_Notification.Title") WriteFailureNotificationText = Language.String(This.Mod.Namespace + ".Write_Failure_Notification.Text") _globalSessionID = "SessionID" # type: str _globalSessionStartTime = "SessionStartTime" # type: str _globalShownWriteFailureNotification = "ShownWriteFailureNotification" # type: str def __init__ (self, loggingRootPath: str, hostNamespace: str = This.Mod.Namespace): """ An object for logging debug information. Logs will be written to a folder named either by the global NeonOcean debugging start time, or the time ChangeLogFile() was last called for this object. :param loggingRootPath: The root path all reports sent to this logger object will be written. :type loggingRootPath: str :param hostNamespace: Errors made by this logger object will show up under this namespace. :type hostNamespace: str """ if not isinstance(loggingRootPath, str): raise Exceptions.IncorrectTypeException(loggingRootPath, "loggingRootPath", (str,)) if not isinstance(hostNamespace, str): raise Exceptions.IncorrectTypeException(hostNamespace, "hostNamespace", (str,)) self.DebugGlobal = Global.GetModule("Debug") if not hasattr(self.DebugGlobal, self._globalSessionID) or not isinstance(getattr(self.DebugGlobal, self._globalSessionID), uuid.UUID): setattr(self.DebugGlobal, self._globalSessionID, uuid.uuid4()) if not hasattr(self.DebugGlobal, self._globalSessionStartTime) or not isinstance(getattr(self.DebugGlobal, self._globalSessionStartTime), datetime.datetime): setattr(self.DebugGlobal, self._globalSessionStartTime, datetime.datetime.now()) if not hasattr(self.DebugGlobal, self._globalShownWriteFailureNotification): setattr(self.DebugGlobal, self._globalShownWriteFailureNotification, False) self.HostNamespace = hostNamespace # type: str self._reportStorage = list() # type: typing.List[Report] self._flushThread = None # type: typing.Optional[threading.Thread] self._loggingRootPath = loggingRootPath # type: str self._loggingDirectoryName = GetDateTimePathString(getattr(self.DebugGlobal, self._globalSessionStartTime)) # type: str self._writeFailureCount = 0 # type: int self._writeFailureLimit = 2 # type: int self._isContinuation = False # type: bool self._sessionInformation = self._CreateSessionInformation() # type: str self._modsDirectoryInformation = self._CreateModsDirectoryInformation() # type: str def Log (self, *args, **kwargs) -> None: raise NotImplementedError() def GetLoggingRootPath (self) -> str: return self._loggingRootPath def GetLoggingDirectoryName (self) -> str: return self._loggingDirectoryName def IsContinuation (self) -> bool: return self._isContinuation def GetSessionID (self) -> uuid.UUID: return getattr(self.DebugGlobal, self._globalSessionID) def GetSessionStartTime (self) -> datetime.datetime: return getattr(self.DebugGlobal, self._globalSessionStartTime) def GetLogStartBytes (self) -> bytes: logStartString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + os.linesep + \ "<LogFile SessionID=\"%s\" SessionStartTime=\"%s\">" % (str(self.GetSessionID()), self.GetSessionStartTime().isoformat()) + os.linesep return logStartString.encode("utf-8") # type: bytes def GetLogEndBytes (self) -> bytes: return (os.linesep + "</LogFile>").encode("utf-8") # type: bytes def ChangeLogFile (self) -> None: """ Change the current directory name for a new one. The new directory name will be the time this method was called. :rtype: None """ self._loggingDirectoryName = GetDateTimePathString(datetime.datetime.now()) self._isContinuation = True self._sessionInformation = self._CreateSessionInformation() self._modsDirectoryInformation = self._CreateModsDirectoryInformation() def Flush (self) -> None: mainThread = threading.main_thread() # type: threading.Thread mainThreadAlive = mainThread.is_alive() # type: bool currentThreadIsMain = mainThread == threading.current_thread() # type: bool if not mainThreadAlive and not currentThreadIsMain: # Unfortunately any daemonic thread's reports will not be written unless they are logged before the last reports from the main thread are written. # At this point, any flush thread started will not finish in time before it all comes to an end. Though the flush thread is not daemonic, Python will # not stop for it while a shutdown is already in progress. The best way of getting any more reports out during this time would be to co-opt the # main thread and make it wait while we write the stored reports. return if self._flushThread is not None: if not mainThreadAlive: if self._flushThread.is_alive(): self._flushThread.join(1) if self._flushThread.is_alive(): return else: self._flushThread = None else: return if not mainThreadAlive: flushThread = self._CreateFlushThread() # type: threading.Thread self._SetFlushThreadUnsafe(flushThread) flushThread.start() flushThread.join(1) else: flushThread = self._CreateFlushThread() # type: threading.Thread try: self._SetFlushThread(flushThread) except ToolsThreading.SimultaneousCallException: return mainThreadStateChanged = not mainThread.is_alive() # type: bool if mainThreadStateChanged: self.Flush() return flushThread.start() def _CreateFlushThread (self) -> threading.Thread: mainThread = threading.main_thread() # type: threading.Thread mainThreadAlive = mainThread.is_alive() # type: bool def _FlushThread () -> None: nonlocal mainThreadAlive mainThreadAlive = mainThread.is_alive() try: while len(self._reportStorage) != 0: reportStorageLength = len(self._reportStorage) # type: int targetReports = self._reportStorage[:reportStorageLength] self._reportStorage = self._reportStorage[reportStorageLength:] filteredReports = self._FilterReports(targetReports) self._LogAllReports(filteredReports) mainThreadAlive = mainThread.is_alive() if not mainThreadAlive: break finally: self._flushThread = None if mainThreadAlive: if len(self._reportStorage) != 0: self.Flush() return threading.Thread(target = _FlushThread, daemon = False) @ToolsThreading.NotThreadSafe(raiseException = True) def _SetFlushThread (self, flushThread: threading.Thread) -> None: self._SetFlushThreadUnsafe(flushThread) def _SetFlushThreadUnsafe (self, flushThread: threading.Thread) -> None: self._flushThread = flushThread def _FilterReports (self, reports: typing.List[Report]) -> typing.List[Report]: return list(reports) def _LogAllReports (self, reports: typing.List[Report]) -> None: raise NotImplementedError() def _CreateSessionInformation (self) -> str: try: installedPacks = list() # type: typing.List[str] for packTuple in common.Pack.items(): # type: typing.Tuple[str, common.Pack] if packTuple[1] == common.Pack.BASE_GAME: continue packAvailable = common.is_available_pack(packTuple[1]) if packAvailable: prefixExpansionPairs = { "EP": "Expansion Pack ", "GP": "Game Pack ", "SP": "Stuff Pack " } # type: typing.Dict[str, str] packText = packTuple[0] # type: str for prefix, prefixExpansion in prefixExpansionPairs.items(): # type: str if packText.startswith(prefix): packText = packText.replace(prefix, prefixExpansion, 1) break installedPacks.append(packText) sessionDictionary = { "SessionID": str(self.GetSessionID()), "SessionStartTime": self.GetSessionStartTime().isoformat(), "IsContinuation": self.IsContinuation(), "OS": platform.system(), "OSVersion": platform.version(), "InstalledPacks": installedPacks } return json.JSONEncoder(indent = "\t").encode(sessionDictionary) except Exception as e: return "Failed to get session information\n" + FormatException(e) def _CreateModsDirectoryInformation (self) -> str: try: modFolderString = os.path.split(Paths.ModsPath)[1] + " {" + os.path.split(Paths.ModsPath)[1] + "}" # type: str for directoryRoot, directoryNames, fileNames in os.walk(Paths.ModsPath): # type: str, list, list depth = 1 if directoryRoot != Paths.ModsPath: depth = len(directoryRoot.replace(Paths.ModsPath + os.path.sep, "").split(os.path.sep)) + 1 # type: int indention = "\t" * depth # type: str newString = "" # type: str for directory in directoryNames: newString += "\n" + indention + directory + " {" + directory + "}" for file in fileNames: newString += "\n" + indention + file + " (" + str(os.path.getsize(os.path.join(directoryRoot, file))) + " B)" if len(newString) == 0: newString = "\n" newString += "\n" modFolderString = modFolderString.replace("{" + os.path.split(directoryRoot)[1] + "}", "{" + newString + "\t" * (depth - 1) + "}", 1) return modFolderString except Exception as e: return "Failed to get mod information\n" + FormatException(e) def _VerifyLogFile (self, logFilePath: str) -> None: logEndBytes = self.GetLogEndBytes() # type: bytes with open(logFilePath, "rb") as logFile: logFile.seek(-len(logEndBytes), os.SEEK_END) if logEndBytes != logFile.read(): raise Exception("The end of the log file doesn't match what was expected.") def _ShowWriteFailureDialog (self, exception: Exception) -> None: Notifications.ShowNotification(queue = True, title = self.WriteFailureNotificationTitle.GetCallableLocalizationString(), text = self.WriteFailureNotificationText.GetCallableLocalizationString(FormatException(exception)), expand_behavior = ui_dialog_notification.UiDialogNotification.UiDialogNotificationExpandBehavior.FORCE_EXPAND, urgency = ui_dialog_notification.UiDialogNotification.UiDialogNotificationUrgency.URGENT)
from NeonOcean.S4.Main import Debug, Language, LoadingShared from NeonOcean.S4.Main.UI import Dialogs from server_commands import argument_helpers from sims import sim_info from sims4 import collections from ui import ui_dialog, ui_dialog_generic, ui_text_input ShowReproductiveInfoCommand: Command.ConsoleCommand ShowSetCycleProgressDialogCommand: Command.ConsoleCommand MakePregnantCommand: Command.ConsoleCommand EndPregnancyCommand: Command.ConsoleCommand ShowSetPregnancyProgressDialogCommand: Command.ConsoleCommand FixDotCycleCommand: Command.ConsoleCommand SetCycleProgressDialogText = Language.String( This.Mod.Namespace + ".Set_Cycle_Progress_Dialog.Text") # type: Language.String SetCycleProgressDialogTitle = Language.String( This.Mod.Namespace + ".Set_Cycle_Progress_Dialog.Title") # type: Language.String SetCycleProgressDialogOkButton = Language.String( This.Mod.Namespace + ".Set_Cycle_Progress_Dialog.Ok_Button", fallbackText="Ok_Button") # type: Language.String SetCycleProgressDialogCancelButton = Language.String( This.Mod.Namespace + ".Set_Cycle_Progress_Dialog.Cancel_Button", fallbackText="Cancel_Button") # type: Language.String SetPregnancyProgressDialogText = Language.String( This.Mod.Namespace + ".Set_Pregnancy_Progress_Dialog.Text") # type: Language.String SetPregnancyProgressDialogTitle = Language.String(
from NeonOcean.S4.Cycle import This from NeonOcean.S4.Main import Language from NeonOcean.S4.Main.Tools import Exceptions from NeonOcean.S4.Main.UI import Notifications from distributor import shared_messages from sims import sim_info from ui import ui_dialog_notification MissedPillNotificationTitle = Language.String( This.Mod.Namespace + ".Birth_Control_Pills.Missed_Pill_Notification.Title" ) # type: Language.String MissedPillNotificationText = Language.String( This.Mod.Namespace + ".Birth_Control_Pills.Missed_Pill_Notification.Text" ) # type: Language.String def ShowMissedPillNotification(targetSimInfo: sim_info.SimInfo, missedPills: int) -> None: """ Show a notification indicating that the target sim forgot to take this many pills. """ if not isinstance(targetSimInfo, sim_info.SimInfo): raise Exceptions.IncorrectTypeException(targetSimInfo, "targetSimInfo", (sim_info.SimInfo, )) if not isinstance(missedPills, int): raise Exceptions.IncorrectTypeException(missedPills, "missedPills", (int, )) notificationArguments = {
class _Logger(DebugShared.Logger): WriteFailureNotificationTitle = Language.String( This.Mod.Namespace + ".Write_Failure_Notification.Title") WriteFailureNotificationText = Language.String( This.Mod.Namespace + ".Write_Failure_Notification.Text") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.LogCount = 0 def Log(self, message, level: Debug.LogLevels, group: str = None, owner: str = None, logStack: bool = False, exception: BaseException = None, frame: types.FrameType = None) -> None: if not isinstance(level, int): raise Exceptions.IncorrectTypeException(level, "level", (int, )) if not isinstance(group, str) and group is not None: raise Exceptions.IncorrectTypeException(group, "group", (str, )) if not isinstance(owner, str) and owner is not None: raise Exceptions.IncorrectTypeException(owner, "owner", (str, )) if not isinstance(exception, BaseException) and exception is not None: raise Exceptions.IncorrectTypeException(exception, "exception", (BaseException, )) if not isinstance(logStack, bool): raise Exceptions.IncorrectTypeException(logStack, "logStack", (bool, )) if isinstance(frame, singletons.DefaultType): frame = None if not isinstance(frame, types.FrameType) and frame is not None: raise Exceptions.IncorrectTypeException(frame, "frame", (types.FrameType, )) if self._writeFailureCount >= self._writeFailureLimit: return if not _loggingEnabled and _loggingEnabled is not None: return if frame is log.DEFAULT: frame = None if exception is None: exception = sys.exc_info()[1] logCount = self.LogCount # type: int self.LogCount += 1 if _logLevel is not None: if level > _logLevel: return report = DebugShared.Report( None, logCount + 1, datetime.datetime.now().isoformat(), str(message), level=level, group=str(group), owner=owner, exception=exception, logStack=logStack, stacktrace=str.join( "", traceback.format_stack(f=frame))) # type: DebugShared.Report self._reportStorage.append(report) if _logInterval == 0: self.Flush() def GetLogSizeLimit(self) -> int: return int(_logSizeLimit * 1000000) def GetLogFilesToBeReported(self) -> typing.List[str]: """ Get the logs to be included in a report archive file. This should be limited to only some of the more recent logs. """ reportingLogFiles = list() # type: typing.List[str] loggingRootPath = self.GetLoggingRootPath() # type: str reportingLogDirectories = list() # type: typing.List[str] latestLogFilePath = os.path.join(loggingRootPath, "Latest.xml") # type: str if os.path.exists(latestLogFilePath): reportingLogFiles.append(latestLogFilePath) for logDirectoryName in reversed( os.listdir(loggingRootPath)): # type: str if len(reportingLogDirectories) >= 10: break logDirectoryPath = os.path.join(loggingRootPath, logDirectoryName) # type: str if not os.path.isdir(logDirectoryPath): continue try: datetime.datetime.strptime( logDirectoryName, "%Y-%m-%d %H.%M.%S.%f").timestamp() # type: float except ValueError: Debug.Log( "Found a directory in a logging namespace that did not meet the naming convention of 'Year-Month-Day Hour.Minute.Second.Microsecond'.\nDirectory Name: %s" % logDirectoryName, This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__, lockIdentifier=__name__ + ":" + str(Python.GetLineNumber()), lockThreshold=1) continue reportingLogDirectories.append(logDirectoryPath) for reportingLogDirectory in reportingLogDirectories: # type: str reportingLogFilePath = os.path.join(reportingLogDirectory, "Log.xml") # type: str if os.path.exists(reportingLogFilePath): reportingLogFiles.append(reportingLogFilePath) reportingSessionFilePath = os.path.join( reportingLogDirectory, "Session.json") # type: str if os.path.exists(reportingSessionFilePath): reportingLogFiles.append(reportingSessionFilePath) reportingModFilePath = os.path.join(reportingLogDirectory, "Mods.txt") # type: str if os.path.exists(reportingModFilePath): reportingLogFiles.append(reportingModFilePath) return reportingLogFiles def _FilterReports( self, reports: typing.List[DebugShared.Report] ) -> typing.List[DebugShared.Report]: def Filter(report: DebugShared.Report) -> bool: if _logLevel is not None: if report.Level > _logLevel: return False return True return list(filter(Filter, reports)) def _LogAllReports(self, reports: typing.List[DebugShared.Report]) -> None: if not _writeChronological and not _writeGroups: return if len(reports) == 0: return chronologicalTextBytes = bytes() # type: bytes groupsTextBytes = dict() # type: typing.Dict[str, bytes] writeTime = datetime.datetime.now().isoformat() # type: str for report in reports: # type: DebugShared.Report group = str(report.Group) # type: str reportTextBytes = report.GetBytes( writeTime=writeTime) # type: bytes if _writeChronological: if len(chronologicalTextBytes) != 0: chronologicalTextBytes += (os.linesep + os.linesep).encode( "utf-8") + reportTextBytes else: chronologicalTextBytes = reportTextBytes if _writeGroups: if group in groupsTextBytes: groupsTextBytes[group] += (os.linesep + os.linesep).encode( "utf-8") + reportTextBytes else: groupsTextBytes[group] = reportTextBytes loggingRoot = self.GetLoggingRootPath() # type: str loggingDirectory = os.path.join( loggingRoot, self.GetLoggingDirectoryName()) # type: str chronologicalFilePath = os.path.join(loggingDirectory, "Log.xml") # type: str chronologicalFirstWrite = False # type: bool latestChronologicalFilePath = os.path.join(loggingRoot, "Latest.xml") # type: str groupsLoggingDirectory = os.path.join(loggingDirectory, "Groups") # type: str sessionFilePath = os.path.join(loggingDirectory, "Session.txt") # type: str modsDirectoryFilePath = os.path.join(loggingDirectory, "Mods Directory.txt") # type: str logSizeLimit = self.GetLogSizeLimit() # type: int logSizeLimitReachedBytes = "<!--Log file size limit reached-->".encode( "utf-8") # type: bytes logStartBytes = self.GetLogStartBytes() # type: bytes logEndBytes = self.GetLogEndBytes() # type: bytes lineSeparatorBytes = (os.linesep + os.linesep).encode( "utf-8") # type: bytes try: if not os.path.exists(loggingDirectory): os.makedirs(loggingDirectory) if _writeGroups: if not os.path.exists(groupsLoggingDirectory): os.makedirs(groupsLoggingDirectory) if not os.path.exists(sessionFilePath): with open(sessionFilePath, mode="w+") as sessionFile: sessionFile.write(self._sessionInformation) if not os.path.exists(modsDirectoryFilePath): with open(modsDirectoryFilePath, mode="w+") as modsFile: modsFile.write(self._modsDirectoryInformation) if _writeChronological: if not os.path.exists(chronologicalFilePath): chronologicalFirstWrite = True else: self._VerifyLogFile(chronologicalFilePath) if chronologicalFirstWrite: if len(logStartBytes) + len(chronologicalTextBytes) + len( logEndBytes) >= logSizeLimit >= 0: chronologicalTextBytes += logSizeLimitReachedBytes with open(chronologicalFilePath, mode="wb+") as chronologicalFile: chronologicalFile.write(logStartBytes) chronologicalFile.write(chronologicalTextBytes) chronologicalFile.write(logEndBytes) if os.path.exists(latestChronologicalFilePath): os.remove(latestChronologicalFilePath) with open(latestChronologicalFilePath, mode="wb+") as latestChronologicalFile: latestChronologicalFile.write(logStartBytes) latestChronologicalFile.write(chronologicalTextBytes) latestChronologicalFile.write(logEndBytes) else: logSize = os.path.getsize( chronologicalFilePath) # type: int if logSizeLimit < 0 or logSize < logSizeLimit: if logSize + len(lineSeparatorBytes) + len( chronologicalTextBytes) + len( logEndBytes) >= logSizeLimit >= 0: chronologicalTextBytes += logSizeLimitReachedBytes with open(chronologicalFilePath, "r+b") as chronologicalFile: chronologicalFile.seek(-len(logEndBytes), os.SEEK_END) chronologicalFile.write(lineSeparatorBytes) chronologicalFile.write(chronologicalTextBytes) chronologicalFile.write(logEndBytes) try: self._VerifyLogFile(latestChronologicalFilePath) with open(latestChronologicalFilePath, "r+b") as latestChronologicalFile: latestChronologicalFile.seek( -len(logEndBytes), os.SEEK_END) latestChronologicalFile.write( lineSeparatorBytes) latestChronologicalFile.write( chronologicalTextBytes) latestChronologicalFile.write(logEndBytes) except: shutil.copy(chronologicalFilePath, latestChronologicalFilePath) for groupName, groupTextBytes in groupsTextBytes.items( ): # type: str, bytes groupFilePath = os.path.join(groupsLoggingDirectory, groupName + ".xml") # type: str groupFirstWrite = False # type: bool if _writeGroups: if not os.path.exists(groupFilePath): groupFirstWrite = True else: self._VerifyLogFile(groupFilePath) if groupFirstWrite: if len(logStartBytes) + len(groupsTextBytes) + len( logEndBytes) >= logSizeLimit >= 0: groupsTextBytes += logSizeLimitReachedBytes with open(groupFilePath, mode="wb+") as groupFile: groupFile.write(logStartBytes) groupFile.write(groupTextBytes) groupFile.write(logEndBytes) else: logSize = os.path.getsize( chronologicalFilePath) # type: int if logSizeLimit < 0 or logSize < logSizeLimit: if logSize + len(lineSeparatorBytes) + len( groupsTextBytes) + len( logEndBytes) >= logSizeLimit >= 0: groupsTextBytes += logSizeLimitReachedBytes with open(groupFilePath, "r+b") as groupFile: groupFile.seek(-len(logEndBytes), os.SEEK_END) groupFile.write((os.linesep + os.linesep).encode("utf-8") + groupTextBytes) groupFile.write(logEndBytes) except Exception as e: self._writeFailureCount += 1 if not getattr(self.DebugGlobal, self._globalShownWriteFailureNotification): self._ShowWriteFailureDialog(e) setattr(self.DebugGlobal, self._globalShownWriteFailureNotification, True) if self._writeFailureCount < self._writeFailureLimit: self.ChangeLogFile() retryingReports = list( filter(lambda filterReport: filterReport.RetryOnError, reports)) # type: typing.List[DebugShared.Report] retryingReportsLength = len(retryingReports) # type: int Debug.Log( "Forced to start a new log file after encountering a write error. " + str(len(reports) - retryingReportsLength) + " reports where lost because of this.", This.Mod.Namespace, Debug.LogLevels.Exception, group=This.Mod.Namespace, owner=__name__, retryOnError=False) for retryingReport in retryingReports: retryingReport.RetryOnError = False if retryingReportsLength != 0: self._LogAllReports(reports) return
import os import typing from NeonOcean.S4.Main import Debug, LoadingShared, Reporting, This, Language, Paths from NeonOcean.S4.Main.UI import Dialogs from NeonOcean.S4.Main.Console import Command from sims4 import commands from ui import ui_dialog ShowPrepareReportLocationDialogCommand: Command.ConsoleCommand PrepareReportLocationDialogTitle = Language.String( This.Mod.Namespace + ".Reporting.Dialogs.Prepare_Report_Location.Title", fallbackText="Prepare_Report_Location.Title") # type: Language.String PrepareReportLocationDialogText = Language.String( This.Mod.Namespace + ".Reporting.Dialogs.Prepare_Report_Location.Text") # type: Language.String PrepareReportLocationDialogCancelButton = Language.String( This.Mod.Namespace + ".Reporting.Dialogs.Prepare_Report_Location.Cancel_Button", fallbackText="Cancel_Button") # type: Language.String PrepareReportLocationDialogDesktopButton = Language.String( This.Mod.Namespace + ".Reporting.Dialogs.Prepare_Report_Location.Desktop_Button", fallbackText="Desktop_Button") # type: Language.String PrepareReportLocationDialogGameUserDataButton = Language.String( This.Mod.Namespace + ".Reporting.Dialogs.Prepare_Report_Location.Game_User_Data_Button", fallbackText="Game_User_Data_Button") # type: Language.String ReportCreatedDialogTitle = Language.String(
import typing from NeonOcean.S4.Cycle import Reproduction, ReproductionShared, This from NeonOcean.S4.Cycle.Females import Shared as FemalesShared, PregnancyTracker from NeonOcean.S4.Main.Tools import Exceptions from NeonOcean.S4.Main import Language from NeonOcean.S4.Main.UI import Notifications from distributor import shared_messages from sims import sim_info PositiveTestResultNotificationTitle = Language.String( This.Mod.Namespace + ".Pregnancy_Test.Positive_Test_Result_Notification.Title" ) # type: Language.String PositiveTestResultNotificationText = Language.String( This.Mod.Namespace + ".Pregnancy_Test.Positive_Test_Result_Notification.Text" ) # type: Language.String NegativeTestResultNotificationTitle = Language.String( This.Mod.Namespace + ".Pregnancy_Test.Negative_Test_Result_Notification.Title" ) # type: Language.String NegativeTestResultNotificationText = Language.String( This.Mod.Namespace + ".Pregnancy_Test.Negative_Test_Result_Notification.Text" ) # type: Language.String def ShowTestResultNotification(targetSimInfo: sim_info.SimInfo) -> None: if not isinstance(targetSimInfo, sim_info.SimInfo):
import typing import clock import date_and_time import services from NeonOcean.S4.Cycle import Dot, This from NeonOcean.S4.Cycle.Females.Cycle import Shared as CycleShared from NeonOcean.S4.Cycle.UI import Resources as CycleUIResources from NeonOcean.S4.Main import Debug, Language from NeonOcean.S4.Main.Tools import Exceptions, TextBuilder from NeonOcean.S4.Main.UI import Notifications from distributor import shared_messages from sims import sim_info from sims4 import localization, resources StatusNotificationTitle = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Title") # type: Language.String StatusNotificationTextTimePeriodStartingToday = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Starting.Today") # type: Language.String StatusNotificationTextTimePeriodStartingTomorrow = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Starting.Tomorrow") # type: Language.String StatusNotificationTextTimePeriodStartingInDays = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Starting.In_Days") # type: Language.String StatusNotificationTextTimePeriodEndingToday = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Ending.Today") # type: Language.String StatusNotificationTextTimePeriodEndingTomorrow = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Ending.Tomorrow") # type: Language.String StatusNotificationTextTimePeriodEndingInDays = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Time_Period.Ending.In_Days") # type: Language.String StatusNotificationTextFollicular = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Follicular") # type: Language.String StatusNotificationTextLuteal = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Luteal") # type: Language.String StatusNotificationTextOvulationStarting = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Ovulation.Starting") # type: Language.String StatusNotificationTextOvulationEnding = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Ovulation.Ending") # type: Language.String StatusNotificationTextMenstruationStarting = Language.String(This.Mod.Namespace + ".Dot.Status_Notification.Text.Menstruation.Starting") # type: Language.String
from NeonOcean.S4.Cycle.Safety import BirthControlPills as SafetyBirthControlPills, Resources as SafetyResources from NeonOcean.S4.Main import Debug, Director, Language from NeonOcean.S4.Main.Interactions.Support import DisableInteraction from NeonOcean.S4.Main.Tools import Exceptions, Patcher, Python from event_testing import results from objects import script_object from objects.components import inventory as ComponentsInventory, types as ComponentsTypes from sims import sim, sim_info from sims4 import resources from sims4.tuning import instance_manager """ If your the creator of wicked whims - [email protected] - It might be better if you do some of these patches. """ DisabledBecauseWickedWhimsInstalledToolTip = Language.String( This.Mod.Namespace + ".Mods.WickedWhims.Disabled_Because_Installed_Tooltip" ) # type: Language.String DisabledBecauseCycleInstalledToolTip = Language.String( This.Mod.Namespace + ".Mods.Cycle.Disabled_Because_Installed_Tooltip") # type: Language.String WickedWhimsBirthControlPillsObjectID = 11109047836475721558 # type: int WickedWhimsCheckCyclesInfoInteractionID = 9855519195140041011 # type: int class _Announcer(Director.Announcer): @classmethod def InstanceManagerOnStart( cls, instanceManager: instance_manager.InstanceManager) -> None: if instanceManager.TYPE != resources.Types.INTERACTION: return
from __future__ import annotations import inspect import sys import types import typing from NeonOcean.S4.Main import Debug, Language, Mods, This from NeonOcean.S4.Main.UI import Dialogs from ui import ui_dialog DialogTitle = Language.String(This.Mod.Namespace + ".Reset.Dialog.Title") # type: Language.String DialogText = Language.String(This.Mod.Namespace + ".Reset.Dialog.Text") # type: Language.String DialogEverythingButton = Language.String( This.Mod.Namespace + ".Reset.Dialog.Everything_Button", fallbackText="Everything_Button") # type: Language.String DialogSettingsButton = Language.String( This.Mod.Namespace + ".Reset.Dialog.Settings_Button", fallbackText="Settings_Button") # type: Language.String DialogCancelButton = Language.String( This.Mod.Namespace + ".Reset.Dialog.Cancel_Button", fallbackText="Cancel_Button") # type: Language.String ConfirmDialogTitle = Language.String( This.Mod.Namespace + ".Reset.Confirm_Dialog.Title") # type: Language.String ConfirmDialogEverythingText = Language.String( This.Mod.Namespace + ".Reset.Confirm_Dialog.Everything_Text") # type: Language.String
class EnglishLanguageHandler(LanguageHandlers.LanguageHandlerBase): IsLanguageHandler = True # type: bool HandlingLanguage = LanguageHandlers.Language.English # type: LanguageHandlers.Language GameIdentifier = "en-us" # type: str TheyThemSetIdentifier = "4CAA6EA8-3D59-4F6B-8842-ABB7F5A5AC27" # type: str TheyThemSetTitle = "They / Them" # type: str ZeZirSetIdentifier = "A5149C53-6B7E-4E26-AA6E-A38DD8AC8743" # type: str ZeZirTitle = "Ze / Zir" ZeHirSetIdentifier = "FB11090A-EC13-4437-832C-B0E0C78B89F9" # type: str ZeHirTitle = "Ze / Hir" XeXemSetIdentifier = "055ECB20-EFD0-477A-91EC-927C947F7E60" # type: str XeXemTitle = "Xe / Xem" EyEmSetIdentifier = "34B839A1-0ADC-42A8-A2E7-6161D6804945" # type: str EyEmTitle = "Ey / Em" ItSetIdentifier = "2C7EC16C-D1BB-496A-987A-D3D95981FB48" # type: str ItTitle = "It" STBLInstanceIDPrefix = "00" # type: str GameLocalizationPackageFileName = "Strings_ENG_US.package" # type: str AskToDoTheyAreFixDialogTitle = Language.String( This.Mod.Namespace + ".Language_Handlers.English.Ask_To_Do_They_Are_Fix_Dialog.Title", fallbackText="Ask_To_Do_They_Are_Fix_Dialog.Title") AskToDoTheyAreFixDialogText = Language.String( This.Mod.Namespace + ".Language_Handlers.English.Ask_To_Do_They_Are_Fix_Dialog.Text", fallbackText="Ask_To_Do_They_Are_Fix_Dialog.Text") AskToDoTheyAreFixDialogYesButton = Language.String( This.Mod.Namespace + ".Language_Handlers.English.Ask_To_Do_They_Are_Fix_Dialog.Yes_Button", fallbackText="Ask_To_Do_They_Are_Fix_Dialog.Yes_Button") AskToDoTheyAreFixDialogNoButton = Language.String( This.Mod.Namespace + ".Language_Handlers.English.Ask_To_Do_They_Are_Fix_Dialog.No_Button", fallbackText="Ask_To_Do_They_Are_Fix_Dialog.No_Button") @classmethod def GetHandlerVersion(cls) -> Version.Version: return This.Mod.Version @classmethod def GetMinimumCacheHandlerVersion(cls) -> typing.Optional[Version.Version]: return None @classmethod def GetGenderTagTextIdentifierPart(cls, tagText: str) -> str: if not isinstance(tagText, str): raise Exceptions.IncorrectTypeException(tagText, "tagText", (str, )) standardizeTagText = tagText.lower().strip(" ") # type: str return { "she's": "she’s", # Standardize apostrophe characters. "he's": "he’s", "she'll": "she’ll", "he'll": "he’ll", "she'd": "she’d", "he'd": "he’d", "mr": "mr.", # Standardize mr and ms. Some of game's mr and ms tags don't have trailing dots, while others do. This handles that inconsistency. "ms": "ms." }.get(standardizeTagText, standardizeTagText) @classmethod def FixGenderTagUsageInconsistency( cls, text: str, cachedGenderTagPairMatches: typing.List[ GenderedLanguage.CachedGenderTagPairMatch] ) -> str: fixedText = "" # type: str unfixedTextStartPosition = None # type: typing.Optional[int] girlBoyTagTexts = {"girl", "boy"} # type: typing.Set[str] for cachedPairMatch in cachedGenderTagPairMatches: # type: GenderedLanguage.CachedGenderTagPairMatch nextUnfixedTextStartPosition = cachedPairMatch.MatchEndPosition # type: int if cachedPairMatch.FirstTagTextLower in girlBoyTagTexts and cachedPairMatch.SecondTagTextLower in girlBoyTagTexts: textAfterGenderTag = text[ cachedPairMatch.MatchEndPosition: cachedPairMatch.MatchEndPosition + 6] # type: str # 6 is the number of characters in 'friend'. if textAfterGenderTag.lower() == "friend": fixedText += text[unfixedTextStartPosition: cachedPairMatch.MatchStartPosition] + \ ("{%s%s.%s%s}{%s%s.%s%s}" % ( cachedPairMatch.FirstTagGender, cachedPairMatch.FirstTagTokenIndexString, cachedPairMatch.FirstTagText, textAfterGenderTag, cachedPairMatch.SecondTagGender, cachedPairMatch.SecondTagTokenIndexString, cachedPairMatch.SecondTagText, textAfterGenderTag )) unfixedTextStartPosition = nextUnfixedTextStartPosition + 6 continue fixedText += text[unfixedTextStartPosition:cachedPairMatch. MatchEndPosition] unfixedTextStartPosition = nextUnfixedTextStartPosition continue fixedText += text[unfixedTextStartPosition:] return fixedText @classmethod def GetStandardPronounSets(cls) -> dict: standardSets = dict() # type: dict standardSets.update(cls._CreateTheyThemSet()) standardSets.update(cls._CreateZeZirSet()) standardSets.update(cls._CreateZeHirSet()) standardSets.update(cls._CreateXeXemSet()) standardSets.update(cls._CreateEyEmSet()) standardSets.update(cls._CreateItSet()) return standardSets @classmethod def GetReservedPronounSetIdentifiers(cls) -> list: return [ cls.TheyThemSetIdentifier, cls.ZeZirSetIdentifier, cls.ZeHirSetIdentifier, cls.XeXemSetIdentifier, cls.EyEmSetIdentifier, cls.ItSetIdentifier ] @classmethod def IsHandlingLanguageSTBLFile(cls, stblInstanceHexID: str) -> bool: if not isinstance(stblInstanceHexID, str): raise Exceptions.IncorrectTypeException(stblInstanceHexID, "stblInstanceHexID", (str, )) return stblInstanceHexID.startswith(cls.STBLInstanceIDPrefix) @classmethod def GetPackLocalizationPackageFilePaths( cls, pack: Sims4Common.Pack) -> typing.List[str]: if not isinstance(pack, Sims4Common.Pack): raise Exceptions.IncorrectTypeException(pack, "pack", (Sims4Common.Pack, )) if game_services.service_manager is None or game_services.service_manager.client_manager is None: return list() if pack in LanguageHandlers.NoLocalizationPackagePacks: return list() if sys.platform.lower() == "darwin": # The game is running on a MAC pc # Note: In these notes "..." only means there is more to the path. # This is something that is done to this path in the paths module.. appAndPacksRootPath = os.path.dirname( os.path. dirname( # ".../Applications/The Sims 4.app/Contents" > ".../Applications" os.path.normpath( os.path.abspath(paths.APP_ROOT) ) # ".../Applications/The Sims 4.app/Contents/" > ".../Applications/The Sims 4.app/Contents )) # type: str gameAppContentsPath = os.path.normpath( os.path.abspath(paths.APP_ROOT)) # type: str if pack == Sims4Common.Pack.BASE_GAME: baseGameLocalizationPackageFilePath = os.path.join( gameAppContentsPath, "Data", "Client", cls.GameLocalizationPackageFileName) # type: str if not os.path.exists(baseGameLocalizationPackageFilePath): Debug.Log( "Could not find the base game's localization package file.\nExpected Location: %s" % baseGameLocalizationPackageFilePath, This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__) return list() return [baseGameLocalizationPackageFilePath] else: packLocalizationPackageFilePath = os.path.join( appAndPacksRootPath, "The Sims 4 Packs", pack.name.upper(), cls.GameLocalizationPackageFileName) # type: str if not os.path.exists(packLocalizationPackageFilePath): Debug.Log( "Could not find a pack's localization package file.\nPack: %s\nExpected Location: %s" % (pack.name, packLocalizationPackageFilePath), This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__) return list() return [packLocalizationPackageFilePath] else: # Assume its running on Windows otherwise. # Note: In these notes "..." only means there is more to the path. # This is something that is done to this path in the paths module. gameRootPath = os.path.dirname( os.path. dirname( # "...\The Sims 4\Game\Bin" > "...\The Sims 4" os.path.normpath( os.path.abspath(paths.APP_ROOT) ) # "...\The Sims 4\Game\Bin\" > "...\The Sims 4\Game\Bin" )) # type: str if pack == Sims4Common.Pack.BASE_GAME: baseGameLocalizationPackageFilePath = os.path.join( gameRootPath, "Data", "Client", cls.GameLocalizationPackageFileName) # type: str if not os.path.exists(baseGameLocalizationPackageFilePath): Debug.Log( "Could not find the base game's localization package file.\nExpected Location: %s" % baseGameLocalizationPackageFilePath, This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__) return list() return [baseGameLocalizationPackageFilePath] else: packLocalizationPackageFilePath = os.path.join( gameRootPath, pack.name.upper(), cls.GameLocalizationPackageFileName) # type: str if not os.path.exists(packLocalizationPackageFilePath): Debug.Log( "Could not find a pack's localization package file.\nPack: %s\nExpected Location: %s" % (pack.name, packLocalizationPackageFilePath), This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__) return list() return [packLocalizationPackageFilePath] @classmethod def GetCustomPronounSetEditableGenderTagPairs(cls) -> typing.List[str]: """ Get a list of gender tag pairs that are editable when creating a custom pronoun set. """ return [ "she|he", "her|him", "her|his", "hers|his", "she’s|he’s", "she’ll|he’ll", "she’d|he’d", "herself|himself" ] @classmethod def GetCustomPronounSetStandardValues(cls) -> typing.Dict[str, str]: """ Get the standard values that custom pronoun sets should have. """ return { "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } @classmethod def GetMoneyString(cls, moneyAmount: numbers.Number) -> str: return "§%s" % str(moneyAmount) @classmethod def GetSimFullNameString(cls, simFirstName: str, simLastName: str) -> str: if not simLastName.isspace(): return "%s %s" % (simFirstName, simLastName) else: return simFirstName @classmethod def AskToApplyAndFixCustomPronounSetPair( cls, modifyingSet: dict, modifyingPairIdentifier: str, chosenPairValue: str, callback: typing.Callable[[bool], None]) -> None: try: if modifyingPairIdentifier == "she’s|he’s": chosenPairValueLower = chosenPairValue.lower().replace( "'", "’") if chosenPairValueLower == "they’re" or chosenPairValueLower == "they’ve": def createFixPronounIsContractionCallback( ) -> typing.Callable[[bool], None]: def fixPronounIsContractionCallback( doFix: bool) -> None: if doFix: modifyingSet[ modifyingPairIdentifier] = cls._GetTheyThemSetPronounIsContraction( ) callback(doFix) return fixPronounIsContractionCallback cls._AskToDoTheyThemFix( createFixPronounIsContractionCallback()) return else: callback(False) except: Debug.Log( "Failed to apply or ask to apply a fix to a custom pronoun set pair.", This.Mod.Namespace, Debug.LogLevels.Exception, group=This.Mod.Namespace, owner=__name__) callback(False) @classmethod def _CreateTheyThemSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.TheyThemSetIdentifier: { "Title": cls.TheyThemSetTitle, "Set": { "she|he": "they", "her|him": "them", "her|his": "their", "hers|his": "theirs", "she’s|he’s": cls._GetTheyThemSetPronounIsContraction(), "she’ll|he’ll": "they’ll", "she’d|he’d": "they’d", "herself|himself": "themself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _GetTheyThemSetPronounIsContraction( cls) -> typing.Union[None, int, str, dict]: return { "Cases": { # Base game 3848466204: ["they’re"], 3315035175: ["they’re"], 1624609554: ["they’re"], 4227368516: ["they’re"], 3466820587: ["they’re"], 2288915427: ["they’re"], 3169408581: ["they’re"], 2678019203: ["they’ve"], 3330177454: ["they’ve", "they’ve"], 1827704107: ["they’re"], 2578737338: ["they’ve"], 3492028680: ["they’re"], 1333238331: ["they’ve"], 515967586: ["they’ve", "they’ve"], 3561336254: ["they’re"], 3704738005: ["they’re"], 465738403: ["they’re"], 3226398757: ["they’re"], 3141825038: ["they’ve"], 2104253335: ["they’re"], 3228515647: ["they’re"], 755536319: ["they’re"], 218063767: ["they’re"], 2381004192: ["they’ve"], 526003011: ["they’re"], 260030352: ["they’re"], 1570293922: ["they’re"], 425395394: ["they’re"], 2162386291: ["they’ve"], 2657241850: ["they’re"], 2899699152: ["they’re", "they’ve"], 274559984: ["they’re"], 2192147899: ["they’ve"], 281927073: ["they’re"], 1637670727: ["they’ve"], 4276505725: ["they’ve"], 1519601547: ["they’re"], 3556521145: ["they’ve"], 229207012: ["they’re", "they’re"], 1378797210: ["they’re"], 1945427369: ["they’ve", "they’re"], 762148562: ["they’re"], 490246009: ["they’re"], 2195308598: ["they’re"], 1787343902: ["they’re", "they’ve"], 4181546333: ["they’re"], 3170322154: ["they’re"], 146307187: ["they’ve"], 2828163811: ["they’re"], 527186830: ["they’re"], 879197290: ["they’re"], 1308055271: ["they’re"], 326337872: ["they’re"], 1446312658: ["they’ve"], 3471668398: ["they’re"], 421326762: ["they’re"], 1882135755: ["they’re"], 530741329: ["they’ve"], 642195893: ["they’re"], 765309846: ["they’ve"], 689897592: ["they’ve"], 381123135: ["they’ve"], 3036917095: ["they’ve"], 3616413496: ["they’re"], 2635592640: ["they’ve"], 1638234967: ["they’re"], 256767393: ["they’re"], 1409130717: ["they’re", "they’ve"], 1143457402: ["they’ve"], 3914469433: ["they’re"], 3381386026: ["they’re"], 482107880: ["they’ve"], 3570446735: ["they’re"], 2308263534: ["they’re"], 2606364627: ["they’re"], 2704319052: ["they’ve"], 528741600: ["they’re"], 884298658: ["they’re"], 3433334323: ["they’re"], 2032866090: ["they’re"], 1304979815: ["they’ve"], 3046188260: ["they’re", "they’re"], 2135613228: ["they’re"], 2067337094: ["they’re"], 3657472333: ["they’re"], 900908594: ["they’ve"], 2525873244: ["they’re"], 2799360804: ["they’re"], 2229212067: ["they’re"], 1450177386: ["they’ve"], 1042303010: ["they’re"], 1333533282: ["they’ve"], 2824793300: ["they’re"], 576298777: ["they’re"], 202995658: ["they’ve"], 2914066380: ["they’re"], 3463186294: ["they’ve"], 2353379673: ["they’re"], 1949298448: ["they’re"], 2908419715: ["they’re"], 2730851378: ["they’re"], 53049609: ["they’re"], 1879120390: ["they’ve"], 3960410444: ["they’re"], 1048614018: ["they’re"], 2272745476: ["they’re"], 327268175: ["they’ve"], 2861103239: ["they’re"], 1259997717: ["they’re"], 1248988049: ["they’ve"], 524550395: ["they’re"], 2146515970: ["they’ve"], 2432123903: ["they’ve", "they’re"], 1527763005: ["they’re"], 388163329: ["they’re", "they’re"], 3560232075: ["they’re"], 3398058508: ["they’re"], 793974509: ["they’ve"], 4255087623: ["they’re"], # EP01 1662158783: ["they’re"], # EP04 854349599: ["they’re"], 3541862656: ["they’re"], # EP06 1474885875: ["they’ve", "they’re"], 1220195416: ["they’ve"], 3803402225: ["they’re"], 467930270: ["they’re", "they’re", "they’ve"], 3425777175: ["they’re", "they’ve"], 4051813452: ["they’re"], 2837713442: ["they’re"], 2915358164: ["they’ve", "they’re"], 1388826329: ["they’re"], 1989470220: ["they’ve"], 1692166899: ["they’re"], 1829982328: ["they’re"], 2399145537: ["they’re"], 1021820828: ["they’re"], 3813596509: ["they’re"], 1776187394: ["they’re"], 1063470410: ["they’re"], 2722379016: ["they’re"], 455203967: ["they’re"], 3349965145: ["they’re"], 979735164: ["they’re"], 3272738715: ["they’re"], 2941426802: ["they’re"], 2709371049: ["they’re"], 1106539124: ["they’re"], 518024581: ["they’re"], 256472513: ["they’re"], 741211455: ["they’re"], 2385622025: ["they’re"], 383194337: ["they’re"], 279240105: ["they’re"], 2500858723: ["they’ve"], 357674073: ["they’re"], 3304244932: ["they’re"], 3463122932: ["they’re"], 25995852: ["they’re"], 78585291: ["they’re"], 1126985834: ["they’re"], 1823306900: ["they’ve"], 1088216950: ["they’re"], 1412561533: ["they’ve"], 1037909830: ["they’re"], 3577957337: ["they’re"], 2115736118: ["they’re"], 223668276: ["they’re"], 2873568945: ["they’re"], 4005536516: ["they’ve"], 4289836499: ["they’ve", None, "they’re"], 1269785600: ["they’re"], 4013355759: ["they’re", "they’ve"], 3216902865: ["they’ve"], 1669566961: ["they’ve", "they’ve"], 60213487: [None, None, "they’re"], 1906335606: ["they’re", "they’re"], 1627066167: ["they’ve"], 1282045627: ["they’re"], 3665016674: ["they’ve"], 173055719: [None], 1648700187: ["they’re", "they’ve", "they’ve"], 299471275: ["they’re", "they’ve", "they’ve", "they’ve"], 746070453: ["they’re", "they’ve", "they’ve"], 3257836685: ["they’re", "they’ve"], 2404242656: ["they’re", "they’ve"], 570100257: ["they’re"], 2742046835: ["they’re", "they’re"], 3633050323: ["they’ve"], # EP07 3683304988: ["they’re"], 4083831262: ["they’re"], 2566275947: ["they’re"], 132224273: ["they’re"], 1022143122: ["they’re"], 4239362667: ["they’re"], 155977711: ["they’re"], # EP08 731208129: ["they’re"], 2165543469: ["they’re"], 3812823502: ["they’re"], 2091981419: ["they’re"], 122260999: ["they’re"], 4190474701: ["they’re"], 3072030183: ["they’re"], 1421972396: ["they’re"], 2321907389: ["they’re"], # EP10 1026766317: ["they’ve"], 4141109267: ["they’re"], 829066520: ["they’re"], 1655784957: ["they’ve"], 4124333197: ["they’re"], 4167129096: ["they’re"], 1621259442: ["they’re"], 2657358867: ["they’re"], 1677419905: ["they’re", "they’ve"], 1633334723: ["they’re"], 1597935695: ["they’re"], 2078949162: ["they’re"], 3052483502: ["they’re"], 581190033: ["they’re", "they’re", "they’re"], 1190606639: ["they’re"], 4124192543: ["they’re"], 2129713926: ["they’re"], 774846671: ["they’re"], 1030449092: ["they’re"], # GP06 106949386: ["they’re"], 1145245963: ["they’ve"], 2043703112: ["they’ve"], 639201188: ["they’ve"], # SP06 3465508511: ["they’ve"], 1617138310: ["they’ve"], 819621328: ["they’re"], 2434216330: ["they’re"], 2096424560: ["they’re"], 2170973686: ["they’ve"], # SP11 3637497501: ["they’re"], } } @classmethod def _CreateZeZirSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.ZeZirSetIdentifier: { "Title": cls.ZeZirTitle, "Set": { "she|he": "ze", "her|him": "zir", "her|his": "zir", "hers|his": "zirs", "she’s|he’s": "ze’s", "she’ll|he’ll": "ze’ll", "she’d|he’d": "ze’d", "herself|himself": "zirself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _CreateZeHirSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.ZeHirSetIdentifier: { "Title": cls.ZeHirTitle, "Set": { "she|he": "ze", "her|him": "hir", "her|his": "hir", "hers|his": "hirs", "she’s|he’s": "ze’s", "she’ll|he’ll": "ze’ll", "she’d|he’d": "ze’d", "herself|himself": "hirself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _CreateXeXemSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.XeXemSetIdentifier: { "Title": cls.XeXemTitle, "Set": { "she|he": "xe", "her|him": "xem", "her|his": "xyr", "hers|his": "xyrs", "she’s|he’s": "xe’s", "she’ll|he’ll": "xe’ll", "she’d|he’d": "xe’d", "herself|himself": "xyrself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _CreateEyEmSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.EyEmSetIdentifier: { "Title": cls.EyEmTitle, "Set": { "she|he": "ey", "her|him": "em", "her|his": "eir", "hers|his": "eirs", "she’s|he’s": "ey’s", "she’ll|he’ll": "ey’ll", "she’d|he’d": "ey’d", "herself|himself": "emself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _CreateItSet(cls) -> dict: # noinspection SpellCheckingInspection creatingSet = { cls.ItSetIdentifier: { "Title": cls.ItTitle, "Set": { "she|he": "it", "her|him": "it", "her|his": "its", "hers|his": "its", "she’s|he’s": "it’s", "she’ll|he’ll": "it’ll", "she’d|he’d": "it’d", "herself|himself": "itself", "ms.|mr.": "mx.", "girlfriend|boyfriend": "partner", "sister|brother": "sibling", "mother|father": "parent", "grandmother|grandfather": "grandparent", "granddaughter|grandson": "grandchild", "wife|husband": "partner", "daughter|son": "child", "step-daughter|step-son": "step-child", "step-mother|step-father": "step-parent", "stepsister|stepbrother": "step-sibling", "great-granddaughter|great-grandson": "great-grandchild", "great-grandmother|great-grandfather": "great-grandparent", "half-sister|half-brother": "half-sibling", } } } return creatingSet @classmethod def _AskToDoTheyThemFix(cls, callback: typing.Callable[[bool], None]) -> None: def createDialogCallback() -> typing.Callable: def dialogCallback( shownDialog: ui_dialog.UiDialogOkCancel) -> None: callback(shownDialog.accepted) return dialogCallback dialogArguments = { "title": cls.AskToDoTheyAreFixDialogTitle.GetCallableLocalizationString(), "text": cls.AskToDoTheyAreFixDialogText.GetCallableLocalizationString(), "text_ok": cls.AskToDoTheyAreFixDialogYesButton.GetCallableLocalizationString( ), "text_cancel": cls.AskToDoTheyAreFixDialogNoButton.GetCallableLocalizationString( ) } # type: typing.Dict[str, ...] UIDialogs.ShowOkCancelDialog(callback=createDialogCallback(), queue=False, **dialogArguments)
import json import os import pathlib import re import shutil import typing import uuid import services from NeonOcean.S4.Main import Debug, Language, Mods, Paths, Saving, This from NeonOcean.S4.Main.Saving import SaveShared from NeonOcean.S4.Main.Tools import Exceptions, FileSystem from NeonOcean.S4.Main.UI import Notifications from ui import ui_dialog_notification FailureNotificationsTitle = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Title") # type: Language.String FailureNotificationsLoadText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Load_Text") # type: Language.String FailureNotificationsSaveText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Save_Text") # type: Language.String FailureNotificationsCommitText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Commit_Text") # type: Language.String FailureNotificationsModLoadText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Mod_Load_Text") # type: Language.String FailureNotificationsModSaveText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Mod_Save_Text") # type: Language.String FailureNotificationsModUnloadText = Language.String(This.Mod.Namespace + ".Saving.Failure_Notifications.Mod_Unload_Text") # type: Language.String WarningNotificationsTitle = Language.String(This.Mod.Namespace + ".Saving.Warning_Notifications.Title") # type: Language.String WarningNotificationsMismatchGUIDText = Language.String(This.Mod.Namespace + ".Saving.Warning_Notifications.Mismatch_GUID_Text") # type: Language.String WarningNotificationsMismatchGameTickText = Language.String(This.Mod.Namespace + ".Saving.Warning_Notifications.Mismatch_Game_Tick_Text") # type: Language.String _registeredSavingObjects = list() # type: typing.List[Saving.SaveBase] _maximumBackups = 5 # type: int
from __future__ import annotations import typing from NeonOcean.S4.Main import Language, This, DistributionShared from NeonOcean.S4.Main.UI import Dialogs, Generic, Resources as UIResources from sims4 import localization, resources from ui import ui_dialog, ui_dialog_picker UpdatesListTitle = Language.String(This.Mod.Namespace + ".Distribution.Updates_List.Title") UpdatesListText = Language.String(This.Mod.Namespace + ".Distribution.Updates_List.Text") UpdatesListRowText = Language.String(This.Mod.Namespace + ".Distribution.Updates_List.Row_Text") UpdatesListRowDescription = Language.String( This.Mod.Namespace + ".Distribution.Updates_List.Row_Description") UpdatesListRowDescriptionReleaseType = Language.String( This.Mod.Namespace + ".Distribution.Updates_List.Row_Description.Release_Type") UpdatesListRowDescriptionPreviewType = Language.String( This.Mod.Namespace + ".Distribution.Updates_List.Row_Description.Preview_Type") def ShowUpdatesList( updatedMods: typing.List[DistributionShared.UpdateInformation] ) -> None: dialogArguments = { "title": UpdatesListTitle.GetCallableLocalizationString(), "text": UpdatesListText.GetCallableLocalizationString()
from __future__ import annotations import enum_lib from NeonOcean.S4.Cycle import This from NeonOcean.S4.Main import Language ReproductiveSystemBuffReason = Language.String( This.Mod.Namespace + ".Buffs_Reasons.Reproductive_System") # type: Language.String class BuffRarity(enum_lib.IntFlag): Common = 1 # type: BuffRarity Uncommon = 2 # type: BuffRarity Rare = 4 # type: BuffRarity VeryRare = 8 # type: BuffRarity