def __init__(self, description="A curved line graph.", **kwargs): super().__init__( description=description, points=tunable.TunableList( description="The points that make up this curved line.", tunable=TunableCurvePoint()), **kwargs)
class ByTypeRegistrationInformation(RegistrationInformation): FACTORY_TUNABLES = { "Types": tunable.TunableList( description= "The types of game object that the interaction should be attached to. A corresponding type determiner python function needs to also exist and be setup for object registration to occur.", tunable=tunable.Tunable(tunable_type=str, default="Everything")) } Types: typing.Tuple[str, ...] def GetRelevantObjectTypes(self) -> typing.Set[str]: """ Get a list of the types of game object that the interaction should be attached to. """ return set(self.Types)
class ByDefinitionInstanceIDRegistrationInformation(RegistrationInformation): FACTORY_TUNABLES = { "InstanceIDs": tunable.TunableList( description= "The instance id of any game object definition that the interaction should be attached to. The interaction would be attached to the definition's game object, it may be simpler to just designate the game object its self.", tunable=tunable.Tunable(tunable_type=int, default=0)) } InstanceIDs: typing.Tuple[int, ...] def GetRelevantDefinitionInstanceIDs(self) -> typing.Set[int]: """ Get a list of instances ids that point to the game object definitions that the interaction should be attached to. """ return set(self.InstanceIDs)
class ByObjectInstanceIDRegistrationInformation(RegistrationInformation): FACTORY_TUNABLES = { "InstanceIDs": tunable.TunableList( description= "The instance id of any game objects that this interaction should be attached to.", tunable=tunable.Tunable(tunable_type=int, default=0)) } InstanceIDs: typing.Tuple[int, ...] def GetRelevantObjectInstanceIDs(self) -> typing.Set[int]: """ Get a list of instances ids that point to the game objects that the interaction should be attached to. """ return set(self.InstanceIDs)
class UseReductionOptions(UseReductionBase, tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): FACTORY_TUNABLES = { "Options": tunable.TunableList( tunable = tunable.TunableVariant( inventoryStatistic = UseReductionInventoryStatistic.TunableFactory(), inventoryObject = UseReductionInventoryObject.TunableFactory(), ) ) } Options: typing.Tuple[UseReductionBase] def GetUseCount (self, targetSimInfo: sim_info.SimInfo) -> int: """ Get the number of uses of this woohoo safety method the target sim has left. :param targetSimInfo: The sim who we are getting the use count of this safety method for. :type targetSimInfo: sim_info.SimInfo :return: The number of uses of this woohoo safety method the target sim has left. :rtype: bool """ if not isinstance(targetSimInfo, sim_info.SimInfo): raise Exceptions.IncorrectTypeException(targetSimInfo, "targetSimInfo", (sim_info.SimInfo,)) useCount = 0 # type: int for option in self.Options: # type: UseReductionBase useCount += option.GetUseCount(targetSimInfo) return useCount def RemoveUse (self, targetSimInfo: sim_info.SimInfo) -> bool: """ Remove one or more uses from this woohoo safety method. :param targetSimInfo: The sim who we are removing a use of this safety method for. :type targetSimInfo: sim_info.SimInfo :return: Whether or not we could remove a use from the target sim. :rtype: bool """ for option in self.Options: # type: UseReductionBase if option.RemoveUse(targetSimInfo): return True return False
class WoohooSafetyMethod(tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): class Requirement: def RequirementMet (self, targetSim: sim.Sim) -> bool: """ Whether or not the target sim meets the requirements. """ raise NotImplementedError() class ObjectRequirement(Requirement, tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): FACTORY_TUNABLES = { "RequiredObject": tunable.TunableReference(description = "A sim must have this object in their inventory in order to meet this requirement.", manager = services.get_instance_manager(resources.Types.OBJECT), pack_safe = True), "RequiredObjectTests": tests.TunableTestSet(description = "A set of tests the object must pass in order to this requirement to be meet.") } RequiredObject: typing.Optional[definition.Definition] RequiredObjectTests: tests.CompoundTestList def RequirementMet (self, targetSim: sim.Sim) -> bool: """ Whether or not the target sim meets the requirements. """ if not isinstance(targetSim, sim.Sim): raise Exceptions.IncorrectTypeException(targetSim, "targetSim", (sim.Sim,)) if self.RequiredObject is None: return False inventoryComponent = targetSim.get_component(ComponentsTypes.INVENTORY_COMPONENT) # type: ComponentsInventory.InventoryComponent if not inventoryComponent.has_item_with_definition(self.RequiredObject): return False else: if len(self.RequiredObjectTests) == 0: return True for matchingObject in inventoryComponent.get_items_with_definition_gen(self.RequiredObject): # type: game_object.GameObject requiredObjectResolver = resolver.SingleObjectResolver(matchingObject) testResults = self.RequiredObjectTests.run_tests(requiredObjectResolver) # type: typing.Optional[TestingUnit.TestResult] if not testResults: return False return True class PerformanceTypeInfo(tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): FACTORY_TUNABLES = { "ArrivingPercentage": tunable.TunableVariant( description = "The options to select the percentage of sperm that will arrive for this performance type. These values should be from 0 to 1.", default = "fixed", fixed = tunable.Tunable(description = "A fixed arriving percentage for this performance type.", tunable_type = float, default = 0.5), random = tunable.TunableInterval(description = "An arriving percentage, between the upper and lower bounds, will be randomly selected.", tunable_type = float, default_lower = 0.5, default_upper = 0.5, minimum = 0, maximum = 1), normalDistribution = Distribution.TunableNormalDistribution(description = "An arriving percentage will be randomly selected based on a normal distribution.", meanDefault = 0.5, standardDeviationDefault = 0), ) } ArrivingPercentage: typing.Union[float, tunable.TunableInterval, Distribution.NormalDistribution] def GenerateSpermArrivingPercentage (self, seed: typing.Optional[int] = None) -> float: """ Get the percentage of sperm that should get past this woohoo device and make it from one sim to another. This will return a number from 0 to 1. """ if seed is None: seed = random.randint(-1000000000, 1000000000) # type: int if not isinstance(seed, int): raise Exceptions.IncorrectTypeException(seed, "seed", (int, None)) if isinstance(self.ArrivingPercentage, tunable.TunedInterval): arrivingPercentage = self.ArrivingPercentage.random_float(seed = seed + -712214524) # type: float elif isinstance(self.ArrivingPercentage, Distribution.NormalDistribution): arrivingPercentage = self.ArrivingPercentage.GenerateValue(seed = seed + 38190718, minimum = 0, maximum = 1) # type: float else: arrivingPercentage = self.ArrivingPercentage return arrivingPercentage FACTORY_TUNABLES = { "Requirements": tunable.TunableList( description = "A list of requirements to determine if a sim can use this method. Requirements may be split into groups, only one group needs to be valid for a sim to met the requirements. If there are no requirements we will assume this method can always be used.", tunable = tunable.TunableList( description = "An individual group for requirements. For this method to be usable, every criteria in at least one of these groups must be met.", tunable = tunable.TunableVariant( objectRequirement = ObjectRequirement.TunableFactory(), ) ) ), "UsingByDefault": tunable.Tunable(description = "Whether or not sims will use this method if its available by default.", tunable_type = bool, default = True), "PerformanceTypes": tunable.TunableMapping( description = "A set of performance types that denote how effective this safety method was. A performance type will be randomly selected for a woohoo using the 'PerformanceTypeChances' value.", key_type = tunable.Tunable(tunable_type = str, default = "UNKNOWN_PERFORMANCE_TYPE"), value_type = PerformanceTypeInfo.TunableFactory() ), "PerformanceTypeChances": Probability.TunableProbability(description = "The chances that each performance type will be selected. Each option's identifier should correspond with a performance type."), "CompoundingMethod": tunable.Tunable(description = "Whether or not this method may be used at the same time as another method. If more than one non-compound method are to be used, we will select the first method found.", tunable_type = bool, default = True), "HandleUseCommand": tunable.OptionalTunable(description = "A console command that needs to be called after this safety method is used. This command should take the woohoo safety method guid, the inseminated sim id, the source sim id, and the performance type, in that order. The method GUID, inseminated sim id, and source sim id parameters may not actually get valid ids.", tunable = tunable.Tunable(tunable_type = str, default = None)) } Requirements: typing.Tuple[typing.Tuple[Requirement, ...], ...] UsingByDefault: bool PerformanceTypes: typing.Dict[str, PerformanceTypeInfo] PerformanceTypeChances: Probability.Probability CompoundingMethod: bool HandleUseCommand: typing.Optional[str] GUID = None # type: typing.Optional[int] @property def HasRequirement (self) -> bool: """ Whether or not this safety method has requirements. """ if len(self.Requirements) == 0: return False for requirementGroup in self.Requirements: # type: typing.Tuple[WoohooSafetyMethod.Requirement, ...] if len(requirementGroup) != 0: return True return False def GetUniqueSeed (self) -> int: """ Get a unique randomization seed for this woohoo safety method. """ random.seed(self.GUID) return random.randint(-1000000000, 1000000000) # type: int def IsAvailable (self, targetSimInfo: sim_info.SimInfo) -> bool: """ Get whether or not the target sim can use this safety method. This may incorrectly return false for non-instanced sims; we cannot read the inventory of sims not instanced. """ if not isinstance(targetSimInfo, sim_info.SimInfo): raise Exceptions.IncorrectTypeException(targetSimInfo, "targetSimInfo", (sim_info.SimInfo,)) if not self.HasRequirement: return True if not targetSimInfo.is_instanced(): return False targetSim = targetSimInfo.get_sim_instance() # type: sim.Sim for requirementGroup in self.Requirements: # type: typing.Tuple[WoohooSafetyMethod.Requirement, ...] if len(requirementGroup) == 0: continue groupRequirementsMet = True # type: bool for requirement in requirementGroup: # type: WoohooSafetyMethod.Requirement if not requirement.RequirementMet(targetSim): groupRequirementsMet = False break if groupRequirementsMet: return True return False def SelectPerformanceType (self, seed: typing.Optional[int] = None) -> typing.Tuple[str, PerformanceTypeInfo]: """ Randomly select one of the performance type in this woohoo safety method. """ if seed is None: seed = random.randint(-1000000000, 1000000000) # type: int if not isinstance(seed, int): raise Exceptions.IncorrectTypeException(seed, "seed", (int, None)) if len(self.PerformanceTypes) == 0: raise Exception("Could not find any safety method performance type to select.\nGUID: %s" % self.GUID) if len(self.PerformanceTypeChances.Options) == 0: raise Exception("Could not select a performance type 'PerformanceTypeChances' has no options.\nGUID: %s" % self.GUID) performanceType = self.PerformanceTypeChances.ChooseOption(seed = seed + -443757754).Identifier # type: str performanceTypeInfo = self.PerformanceTypes.get(performanceType, None) # type: WoohooSafetyMethod.PerformanceTypeInfo if performanceTypeInfo is None: Debug.Log("Randomly selected performance type that doesn't exist.\nGUID: %s, Performance Type: %s" % (str(self.GUID), performanceType), This.Mod.Namespace, Debug.LogLevels.Error, group = This.Mod.Namespace, owner = __name__) return random.choice(self.PerformanceTypes) return performanceType, performanceTypeInfo def GenerateSpermArrivingPercentage (self, seed: typing.Optional[int] = None) -> float: """ Get the percentage of sperm that should get past this woohoo device and make it from one sim to another. This will return a number from 0 to 1. """ if seed is None: seed = random.randint(-1000000000, 1000000000) # type: int if not isinstance(seed, int): raise Exceptions.IncorrectTypeException(seed, "seed", (int, None)) performanceType, performanceTypeInfo = self.SelectPerformanceType(seed = seed + -443757754) # type: str, WoohooSafetyMethod.PerformanceTypeInfo arrivingPercentage = performanceTypeInfo.GenerateSpermArrivingPercentage(seed + -16160599) # type: float if arrivingPercentage < 0: Debug.Log("Safety method performance type generated an arriving percentage is less than 0.\nGUID: %s, Performance Type: %s" % (str(self.GUID), performanceType), This.Mod.Namespace, Debug.LogLevels.Error, group = This.Mod.Namespace, owner = __name__) arrivingPercentage = 0 if arrivingPercentage > 1: Debug.Log("Safety method performance type generated an arriving percentage is greater than 1.\nGUID: %s, Performance Type: %s" % (str(self.GUID), performanceType), This.Mod.Namespace, Debug.LogLevels.Error, group = This.Mod.Namespace, owner = __name__) arrivingPercentage = 1 return arrivingPercentage def HandlePostUse (self, inseminatedSimInfo: typing.Optional[sim_info.SimInfo], sourceSimInfo: typing.Optional[sim_info.SimInfo], performanceType: str) -> None: """ Handle the after effects of using this woohoo safety method. """ if not isinstance(inseminatedSimInfo, sim_info.SimInfo) and inseminatedSimInfo is not None: raise Exceptions.IncorrectTypeException(inseminatedSimInfo, "inseminatedSimInfo", (sim_info.SimInfo,)) if not isinstance(inseminatedSimInfo, sim_info.SimInfo) and inseminatedSimInfo is not None: raise Exceptions.IncorrectTypeException(inseminatedSimInfo, "inseminatedSimInfo", (sim_info.SimInfo,)) if not isinstance(performanceType, str): raise Exceptions.IncorrectTypeException(performanceType, "performanceType", (str,)) if self.HandleUseCommand is not None: methodGUID = self.GUID if self.GUID is not None else 0 # type: int inseminatedSimID = inseminatedSimInfo.id if inseminatedSimInfo is not None else 0 sourceSimID = sourceSimInfo.id if sourceSimInfo is not None else 0 try: consoleCommand = " ".join((self.HandleUseCommand, str(methodGUID), str(inseminatedSimID), str(sourceSimID), performanceType)) commands.execute(consoleCommand, None) except: Debug.Log("Failed to call handle post use command for woohoo safety method '%s'." % str(self.GUID), This.Mod.Namespace, Debug.LogLevels.Exception, group = This.Mod.Namespace, owner = __name__)
class RegistrationExtension: """ Allows you to setup automatic registration of interactions to objects. """ RegisterAllObjects: bool # noinspection SpellCheckingInspection INSTANCE_TUNABLES = { "ObjectRegistrationInformation": tunable.TunableList(tunable=RegistrationInformationVariant()), } ObjectRegistrationInformation: typing.List[RegistrationInformation] _registeredObjects = dict( ) # type: typing.Dict[typing.Type[script_object.ScriptObject], typing.Set[str]] def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) cls._registeredObjects = dict( ) # type: typing.Dict[typing.Type[script_object.ScriptObject], typing.Set[str]] @classmethod def RegisterToObjects(cls) -> None: """ Add this interaction to the appropriate objects. """ definitionManager = services.definition_manager( ) # type: definition_manager.DefinitionManager objectTypeOrganizer = cls._GetObjectTypeOrganizer( ) # type: typing.Type[RegistrationTypes.ObjectTypeOrganizer] objectsByType = objectTypeOrganizer.GetObjectsByType( ) # type: typing.Dict[str, typing.Set[typing.Type[script_object.ScriptObject]]] for registrationInformation in cls.ObjectRegistrationInformation: # type: RegistrationInformation relevantObjects = set( ) # type: typing.Set[typing.Type[script_object.ScriptObject]] for relevantObjectInstanceID in registrationInformation.GetRelevantObjectInstanceIDs( ): # type: int relevantObjectKey = resources.get_resource_key( relevantObjectInstanceID, resources.Types.OBJECT) matchingObject = definitionManager.types.get( relevantObjectKey, None ) # type: typing.Optional[typing.Type[script_object.ScriptObject]] if matchingObject is None: continue relevantObjects.add(matchingObject) for relevantDefinitionInstanceID in registrationInformation.GetRelevantDefinitionInstanceIDs( ): # type: int matchingDefinition = definitionManager.get( relevantDefinitionInstanceID ) # type: typing.Optional[definition.Definition] if matchingDefinition is None: continue matchingObject = matchingDefinition.cls # type: typing.Type[script_object.ScriptObject] relevantObjects.add(matchingObject) for relevantObjectType in registrationInformation.GetRelevantObjectTypes( ): # type: str matchingObjects = objectsByType.get( relevantObjectType, None ) # type: typing.Optional[typing.Set[typing.Type[script_object.ScriptObject]]] if matchingObjects is None: continue relevantObjects.update(matchingObjects) for relevantObject in relevantObjects: # type: typing.Type[script_object.ScriptObject] registeredInteractionLists = cls._registeredObjects.get( relevantObject, None) # type: typing.Optional[typing.List[str]] if registeredInteractionLists is not None: if registrationInformation.InteractionListAttribute in registeredInteractionLists: continue registrationInformation.RegisterObject(relevantObject, cls) if registeredInteractionLists is not None: registeredInteractionLists.append( registrationInformation.InteractionListAttribute) else: cls._registeredObjects[relevantObject] = { registrationInformation.InteractionListAttribute } @classmethod def _tuning_loaded_callback(cls): superObject = super() if hasattr(superObject, "_tuning_loaded_callback"): # noinspection PyProtectedMember superObject._tuning_loaded_callback() cls._RegisterRegistrationExtendedInteraction() @classmethod def _RegisterRegistrationExtendedInteraction(cls) -> None: _registrationExtensionInteractions.append(cls) @classmethod def _GetObjectTypeOrganizer( cls) -> typing.Type[RegistrationTypes.ObjectTypeOrganizer]: return RegistrationTypes.ObjectTypeOrganizer
def __init__ (self, description = "An object to randomly select from a set of options.", **kwargs): # TODO add the spacings the ea's tunables use to descriptions? super().__init__( description = description, options = tunable.TunableList(description = "The set of options for the probability object.", tunable = TunableOption()), **kwargs)
class CyclePhaseTest(tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): class PhaseState(tunable.HasTunableSingletonFactory, tunable.AutoFactoryInit): class MatchTypes(enum_lib.IntEnum): Whitelist = 1 # type: CyclePhaseTest.PhaseState.MatchTypes Blacklist = 2 # type: CyclePhaseTest.PhaseState.MatchTypes FACTORY_TUNABLES = { "Phase": ToolsTunable.TunablePythonEnumEntry( description="The targeted phase of a menstrual cycle.", enumType=CycleShared.MenstrualCyclePhases, default=CycleShared.MenstrualCyclePhases.Menstruation), "MatchType": ToolsTunable.TunablePythonEnumEntry( description= "Whether the specified phase is whitelisted or blacklisted. If there are no whitelisted states we will assume that all states are valid unless told otherwise by blacklisted states.", enumType=MatchTypes, default=MatchTypes.Whitelist), "StartCompletion": tunable.OptionalTunable(tunable=tunable.TunableRange( description= "The completion percentage for the targeted phase at which this test will start matching.", tunable_type=float, default=0, minimum=0, maximum=1)), "EndCompletion": tunable.OptionalTunable(tunable=tunable.TunableRange( description= "The completion percentage for the targeted phase at which this test will stop matching.", tunable_type=float, default=0, minimum=0, maximum=1)) } Phase: CycleShared.MenstrualCyclePhases MatchType: MatchTypes StartCompletion: typing.Optional[float] EndCompletion: typing.Optional[float] FACTORY_TUNABLES = { "PhaseStates": tunable.TunableList(tunable=PhaseState.TunableFactory( description= "A list of phase states that the menstrual cycle must either match or not match. Letting this be empty indicates that the menstrual cycle will always be valid." )) } PhaseStates: typing.Tuple[PhaseState, ...] def InValidPhase(self, testingCycle: CycleMenstrual.MenstrualCycle) -> bool: """ Get whether or not this cycle is in a valid phase for this test. """ if not isinstance(testingCycle, CycleMenstrual.MenstrualCycle): raise Exceptions.IncorrectTypeException( testingCycle, "testingCycle", (CycleMenstrual.MenstrualCycle, )) if len(self.PhaseStates) == 0: return True validState = True # type: bool hasWhitelistedPhase = False # type: bool inWhitelistedPhase = False # type: bool for phaseState in self.PhaseStates: # type: CyclePhaseTest.PhaseState if phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Whitelist: hasWhitelistedPhase = True if not testingCycle.GetPhaseIsActive(phaseState.Phase): continue phaseCompletion = testingCycle.GetPhaseCompletionPercentage( phaseState.Phase) # type: typing.Optional[float] if phaseCompletion is None: continue if phaseState.StartCompletion is not None and phaseState.StartCompletion > phaseCompletion: continue if phaseState.EndCompletion is not None and phaseState.EndCompletion < phaseCompletion: continue if phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Whitelist: inWhitelistedPhase = True continue elif phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Blacklist: validState = False break if hasWhitelistedPhase and not inWhitelistedPhase: validState = False return validState def TicksUntilValidPhase( self, testingCycle: CycleMenstrual.MenstrualCycle, reproductiveTimeMultiplier: float) -> typing.Optional[int]: """ Get the number of game ticks until the input cycle reaches a valid phase. This will return none if the cycle will never enter a valid phase. :param testingCycle: The menstrual cycle to find valid phases for. :type testingCycle: CycleMenstrual.MenstrualCycle :param reproductiveTimeMultiplier: The reproductive time multiplier used to simulate the testing cycle :type reproductiveTimeMultiplier: float """ if not isinstance(testingCycle, CycleMenstrual.MenstrualCycle): raise Exceptions.IncorrectTypeException( testingCycle, "testingCycle", (CycleMenstrual.MenstrualCycle, )) if len(self.PhaseStates) == 0: return 0 hasWhitelistedPhase = False # type: bool whitelistedTimes = list( ) # type: typing.List[typing.Tuple[float, float]] blacklistedTimes = list( ) # type: typing.List[typing.Tuple[float, float]] for phaseState in self.PhaseStates: if phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Whitelist: hasWhitelistedPhase = True if testingCycle.GetPhaseCompleted(phaseState.Phase): continue timeUntilPhaseStart = testingCycle.GetTimeUntilPhaseStarts( phaseState.Phase) # type: float timeUntilPhaseEnd = testingCycle.GetTimeUntilPhaseEnds( phaseState.Phase) # type: float if timeUntilPhaseEnd < 0: Debug.Log( "A menstrual cycle indicated a phase was not complete, but its end time was %s minutes ago. Phase: %s" % (str(timeUntilPhaseEnd), str(phaseState.Phase)), This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__, lockIdentifier=__name__ + ":" + str(Python.GetLineNumber())) continue phaseLength = timeUntilPhaseEnd - timeUntilPhaseStart # type: float if phaseLength <= 0: Debug.Log( "Calculated a menstrual cycle phase length as less than or equal to 0. Phase: %s" % str(phaseState.Phase), This.Mod.Namespace, Debug.LogLevels.Warning, group=This.Mod.Namespace, owner=__name__, lockIdentifier=__name__ + ":" + str(Python.GetLineNumber())) continue if phaseState.StartCompletion is None: listedStartTime = timeUntilPhaseStart else: listedStartTime = timeUntilPhaseStart + phaseLength * phaseState.StartCompletion if phaseState.EndCompletion is None: listedEndTime = timeUntilPhaseEnd else: listedEndTime = timeUntilPhaseStart + phaseLength * phaseState.EndCompletion if phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Whitelist: whitelistedTimes.append((listedStartTime, listedEndTime)) elif phaseState.MatchType == CyclePhaseTest.PhaseState.MatchTypes.Blacklist: blacklistedTimes.append((listedStartTime, listedEndTime)) soonestValidTime = None # type: typing.Optional[float] def setTimeIfSooner(testingValidTime: float) -> None: nonlocal soonestValidTime if soonestValidTime is None: soonestValidTime = testingValidTime if soonestValidTime < testingValidTime: soonestValidTime = testingValidTime if hasWhitelistedPhase: for whitelistedStartTime, whitelistedEndTime in whitelistedTimes: # type: float, float if soonestValidTime is not None and whitelistedStartTime >= soonestValidTime: continue validTime = whitelistedStartTime # type: float intervalBlacklisted = False # type: bool for blackListedStartTime, blackListedEndTime in blacklistedTimes: # type: float, float if blackListedStartTime <= validTime and blackListedEndTime >= whitelistedEndTime: intervalBlacklisted = True break if blackListedStartTime <= validTime: validTime = blackListedEndTime if not intervalBlacklisted: setTimeIfSooner(max(validTime, 0.0)) if soonestValidTime == 0: break else: for blackListedStartTime, blackListedEndTime in blacklistedTimes: # type: float, float if soonestValidTime is not None and blackListedEndTime >= soonestValidTime: continue blacklistContinues = False # type: bool for otherBlackListedStartTime, otherBlackListedEndTime in blacklistedTimes: # type: float, float if otherBlackListedStartTime <= blackListedEndTime < otherBlackListedEndTime: blacklistContinues = True pass if not blacklistContinues: setTimeIfSooner(max(blackListedEndTime, 0.0)) if soonestValidTime == 0: break if soonestValidTime is None: return None else: return ReproductionShared.ReproductiveMinutesToTicks( soonestValidTime, reproductiveTimeMultiplier)