def _SimulateInternal (self, simulation: ReproductionShared.Simulation, ticks: int, reproductiveTimeMultiplier: typing.Union[float, int]) -> None: ageTicks = ReproductionShared.ReproductiveMinutesToTicks(self.Age, reproductiveTimeMultiplier) # type: typing.Union[float, int] decayTick = ReproductionShared.ReproductiveMinutesToTicks(self.Lifetime, reproductiveTimeMultiplier) # type: typing.Union[float, int] decaying = False # type: bool decayed = False # type: bool if ageTicks < decayTick <= (ageTicks + ticks): decaying = True if decayTick <= (ageTicks + ticks): decayed = True if not decayed: decayingAmount = self.DecayingTicks(ticks, reproductiveTimeMultiplier) # type: int else: decayingAmount = self.SpermCount # type: int self.Age = ReproductionShared.TicksToReproductiveMinutes(ageTicks + ticks, reproductiveTimeMultiplier) self.SpermCount -= decayingAmount if decaying: if self.DecayedCallback is None: Debug.Log("Missing callback to be triggered on sperm decay.", This.Mod.Namespace, Debug.LogLevels.Warning, group = This.Mod.Namespace, owner = __name__, lockIdentifier = __name__ + ":" + str(Python.GetLineNumber())) else: self.DecayedCallback(self)
def _PlanSimulationInternal(self, simulation: ReproductionShared.Simulation, reproductiveTimeMultiplier: float) -> None: quickMode = Settings.QuickMode.Get() # type: bool if not self.Fertilized: decayTick = ReproductionShared.ReproductiveMinutesToTicks( self.TimeRemaining, reproductiveTimeMultiplier) # type: int if decayTick <= 0: decayTick = 1 if simulation.RemainingTicks >= decayTick: simulation.Schedule.AddPoint(decayTick) else: if quickMode: if simulation.RemainingTicks >= 1: simulation.Schedule.AddPoint(1) else: implantationTick = ReproductionShared.ReproductiveMinutesToTicks( self.TimeUntilImplantation, reproductiveTimeMultiplier) # type: int if simulation.RemainingTicks >= implantationTick: simulation.Schedule.AddPoint(implantationTick)
def _CycleSimulationPhase (self, simulation: ReproductionShared.Simulation, ticks: int) -> None: reproductiveTimeMultiplier = self.ReproductiveTimeMultiplier # type: typing.Union[float, int] simulatingMinutes = ReproductionShared.TicksToReproductiveMinutes(ticks, reproductiveTimeMultiplier) # type: typing.Union[float, int] simulationMemoryKey = self.SimulationMemoryKey simulationMemoryExists = simulationMemoryKey in simulation.Memory # type: bool simulationMemory = simulation.Memory.get(simulationMemoryKey, CycleTrackerSimulationMemory()) # type: CycleTrackerSimulationMemory if self.TimeSinceLastCycle is not None: self.TimeSinceLastCycle += simulatingMinutes if self.CurrentCycle is not None: cycleTicksRemaining = ReproductionShared.ReproductiveMinutesToTicks(self.CurrentCycle.TimeRemaining, reproductiveTimeMultiplier) # type: typing.Union[float, int] if cycleTicksRemaining < ticks: Debug.Log("Simulation stepped over the end of a cycle by %s ticks, this may cause lost time for the tracking sim.\n%s" % (str(ticks - cycleTicksRemaining), self.DebugInformation), This.Mod.Namespace, Debug.LogLevels.Warning, group = This.Mod.Namespace, owner = __name__, lockIdentifier = __name__ + ":" + str(Python.GetLineNumber()), lockReference = self.TrackingSystem) self.CurrentCycle.Simulate(simulation, ticks, reproductiveTimeMultiplier) else: if simulationMemory.CycleStartTesting is None: Debug.Log("Expected the 'CycleStartTesting' to be in the simulation memory, but all we found was None.\n" + self.DebugInformation, This.Mod.Namespace, Debug.LogLevels.Warning, group = This.Mod.Namespace, owner = __name__, lockIdentifier = __name__ + ":" + str(Python.GetLineNumber()), lockReference = self.TrackingSystem) simulationMemory.CycleStartTesting = self.DoCycleStartTesting() elif not simulationMemoryExists: simulationMemory.CycleStartTesting = self.DoCycleStartTesting() if simulationMemory.CycleStartTesting.GetCanStart(): timeUntilCycleStart = simulationMemory.CycleStartTesting.GetTimeUntilStart() # type: typing.Optional[float] cycleStartPlanned = timeUntilCycleStart is not None # type: bool if cycleStartPlanned: ticksUntilCycleStart = ReproductionShared.ReproductiveMinutesToTicks(timeUntilCycleStart, reproductiveTimeMultiplier) # type: int else: ticksUntilCycleStart = 1 # type: int if ticksUntilCycleStart <= ticks: if cycleStartPlanned and ticksUntilCycleStart < ticks: Debug.Log("Simulation stepped over the start of a cycle by %s ticks, this may cause lost time for the tracking sim.\n%s" % (str(ticks - ticksUntilCycleStart), self.DebugInformation), This.Mod.Namespace, Debug.LogLevels.Warning, group = This.Mod.Namespace, owner = __name__, lockIdentifier = __name__ + ":" + str(Python.GetLineNumber()), lockReference = self.TrackingSystem) cycleTypeIdentifier = simulationMemory.CycleStartTesting.GetCycleTypeIdentifier() # type: str cycle = self.GenerateCycle(cycleTypeIdentifier) self.CycleStartTestingSeed = None simulationMemory.CycleStartTesting = None self.CurrentCycle = cycle else: simulationMemory.CycleStartTesting.IncreaseTimeSinceTest(simulatingMinutes) if not simulationMemoryExists: simulation.Memory[simulationMemoryKey] = simulationMemory
def _SimulateInternal (self, ticks: int) -> None: if ticks <= 0: return if self.TimeSinceCycleStart is None: return cycleReproductiveTimeMultiplier = _GetCycleReproductiveTimeMultiplier() # type: float lastTimeSinceCycleStart = self.TimeSinceCycleStart # type: float lastTickSinceCycleStart = ReproductionShared.ReproductiveMinutesToTicks(lastTimeSinceCycleStart, cycleReproductiveTimeMultiplier) # type: int nextTickSinceCycleStart = lastTickSinceCycleStart + ticks # type: int nextTimeSinceCycleStart = ReproductionShared.TicksToReproductiveMinutes(nextTickSinceCycleStart, cycleReproductiveTimeMultiplier) # type: float if self.ShowFertilityNotifications and self.Enabled: currentCycleState = self._GetCycle(lastTimeSinceCycleStart) # type: DotCycle currentTicksUntilOvulationStarts = ReproductionShared.GameMinutesToTicks(currentCycleState.GetTimeUntilPhaseStarts(CycleShared.MenstrualCyclePhases.Ovulation)) # type: int ticksBeforeOvulationToNotify = ReproductionShared.GameMinutesToTicks(_GetSpermHalfLifeTime()) # type: int if ticksBeforeOvulationToNotify < self.MinimumTicksBeforeOvulationToNotify: ticksBeforeOvulationToNotify = self.MinimumTicksBeforeOvulationToNotify if currentTicksUntilOvulationStarts > 0 and ticksBeforeOvulationToNotify < currentTicksUntilOvulationStarts: nextCycleState = self._GetCycle(nextTimeSinceCycleStart) # type: DotCycle nextTicksUntilOvulationStarts = ReproductionShared.GameMinutesToTicks(nextCycleState.GetTimeUntilPhaseStarts(CycleShared.MenstrualCyclePhases.Ovulation)) # type: int if ticksBeforeOvulationToNotify > nextTicksUntilOvulationStarts: UIDot.ShowFertilityNotification(self.TargetSimInfo) self.TimeSinceCycleStart = nextTimeSinceCycleStart
def _PlanSimulation (self, simulation: ReproductionShared.Simulation) -> None: super()._PlanSimulation(simulation) reproductiveTimeMultiplier = self.ReproductiveTimeMultiplier # type: typing.Union[float, int] simulationMemory = CycleTrackerSimulationMemory() # type: CycleTrackerSimulationMemory if self.CurrentCycle is None: cycleStartTesting = self.DoCycleStartTesting() # type: CycleEvents.CycleStartTestingArguments if cycleStartTesting.GetCanStart(): timeUntilCycleStart = cycleStartTesting.GetTimeUntilStart() # type: typing.Optional[float] cycleStartPlanned = timeUntilCycleStart is not None # type: bool if cycleStartPlanned: ticksUntilCycleStart = ReproductionShared.ReproductiveMinutesToTicks(timeUntilCycleStart, reproductiveTimeMultiplier) # type: int else: ticksUntilCycleStart = 1 # type: int if simulation.RemainingTicks >= ticksUntilCycleStart: simulation.Schedule.AddPoint(ticksUntilCycleStart) simulationMemory.CycleStartTesting = cycleStartTesting else: self.CurrentCycle.PlanSimulation(simulation, reproductiveTimeMultiplier) simulation.Memory[self.SimulationMemoryKey] = simulationMemory
def _PlanSimulationInternal (self, simulation: ReproductionShared.Simulation, reproductiveTimeMultiplier: typing.Union[float, int]) -> None: decayTick = ReproductionShared.ReproductiveMinutesToTicks(self.TimeRemaining, reproductiveTimeMultiplier) # type: int if decayTick <= 0: decayTick = 1 if simulation.RemainingTicks >= decayTick: simulation.Schedule.AddPoint(decayTick)
def DecayingTicks (self, ticks: int, reproductiveTimeMultiplier: typing.Union[float, int]) -> int: """ Get the amount of sperm cells that will decay within this many ticks. """ if not isinstance(ticks, (int,)): raise Exceptions.IncorrectTypeException(ticks, "ticks", (int,)) if not isinstance(reproductiveTimeMultiplier, (float, int)): raise Exceptions.IncorrectTypeException(reproductiveTimeMultiplier, "reproductiveTimeMultiplier", (float, int)) if ticks < 0: raise ValueError("The parameter 'ticks' cannot be less than 0.") if reproductiveTimeMultiplier <= 0: raise ValueError("The parameter 'reproductiveTimeMultiplier' cannot be less than or equal to 0.") if self.SpermCount == 0: return 0 if ticks == 0: return 0 currentAgeTicks = ReproductionShared.ReproductiveMinutesToTicks(self.Age, reproductiveTimeMultiplier) # type: int currentAge = ReproductionShared.TicksToReproductiveMinutes(currentAgeTicks, reproductiveTimeMultiplier) # type: typing.Union[float, int] # This is slightly faster than getting using the GetClosestPreciseReproductiveMinute function. nextAgeTicks = currentAgeTicks + ticks # type: typing.Union[float, int] nextAge = ReproductionShared.TicksToReproductiveMinutes(nextAgeTicks, reproductiveTimeMultiplier) # type: typing.Union[float, int] lifeTimeTick = ReproductionShared.ReproductiveMinutesToTicks(self.Lifetime, reproductiveTimeMultiplier) if nextAgeTicks >= lifeTimeTick: return self.SpermCount currentPercentageRemaining = 1.0 - self.LifetimeDistribution.CumulativeDistribution(currentAge) # type: typing.Union[float, int] nextPercentageRemaining = 1.0 - self.LifetimeDistribution.CumulativeDistribution(nextAge) # type: typing.Union[float, int] percentageRemainingChange = currentPercentageRemaining - nextPercentageRemaining # type: typing.Union[float, int] originalSpermCount = int(self.SpermCount / currentPercentageRemaining) # type: int decayingSpermCount = int(originalSpermCount * percentageRemainingChange) # type: int if decayingSpermCount < 0: Debug.Log("Calculated a decaying sperm count of less than zero (%s)." % decayingSpermCount, This.Mod.Namespace, Debug.LogLevels.Warning, group = This.Mod.Namespace, owner = __name__, lockIdentifier = __name__ + ":" + str(Python.GetLineNumber())) return int(originalSpermCount * percentageRemainingChange)
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)
def TicksSinceLastCycle (self) -> typing.Union[float, int, None]: """ The amount of game ticks since the last cycle ended. This will be None if the sim has not had a cycle yet. """ return ReproductionShared.ReproductiveMinutesToTicks(self.TimeSinceLastCycle, self.ReproductiveTimeMultiplier)