oldschool-toontown/toontown/ai/PhasedHolidayAI.py
2024-07-14 15:28:28 -05:00

140 lines
5.6 KiB
Python

import datetime
import time
from direct.directnotify import DirectNotifyGlobal
from toontown.ai import HolidayBaseAI
from toontown.toonbase import ToontownGlobals
from toontown.ai import DistributedResistanceEmoteMgrAI
class StartAndEndTime:
def __init__(self, startTime, endTime):
"""Keep track of start and end datetimes, in a struct."""
self.start = startTime
self.end = endTime
def isInBetween(self, phaseTime):
"""Returns true if phaseTime is in between start and end times.
Note that it returns false if it is equal."""
result = False
if self.start < phaseTime and phaseTime < self.end:
result = True
return result
def isInBetweenInclusive(self, phaseTime):
"""Returns true if phaseTime is in between start and end times.
Note that it returns true if it is equal."""
result = False
if self.start <= phaseTime and phaseTime <= self.end:
result = True
return True
def __repr__(self):
return self.__str__()
def __str__(self):
return ("(%s %s)" % (self.start, self.end))
class PhasedHolidayAI(HolidayBaseAI.HolidayBaseAI):
"""This is the base class for holidays which have different phases.
They ramp up over time, such as hydrant zero, silly meter.
While each phase could have been implemented as a different holiday,
this makes sure the start and end times for each phase match up."""
# WARNING phased holidays with multiple start and end times have not been fully tested
# RAU coded with it in mind, but edge cases can easily slip through
notify = DirectNotifyGlobal.directNotify.newCategory(
'PhasedHolidayAI')
def __init__(self, air, holidayId, startAndEndTupleList, phaseDates):
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
self.oldPhase = 0
self.curPhase = 0
self.phaseDates = phaseDates
self.startAndEndTimes = self.convertStartAndEndListToDateTimes(startAndEndTupleList)
self.sanityCheckPhaseDates()
# we assume phase 0 is before phaseDates[0],
# phase 1 is between phaseDates[0], phaseDates[1]
# the last phase is after phaseDates[-1]
# TODO should we have the FSM here or in the child class?
# Lets leave it in the child class in case it's really simple
def convertStartAndEndListToDateTimes(self, startAndEndTupleList):
"""Convert start and end times to a more manageable class."""
result = []
for startAndEnd in startAndEndTupleList:
startInfo = startAndEnd[0]
startTime = datetime.datetime(startInfo[0], startInfo[1], startInfo[2],
startInfo[3], startInfo[4], startInfo[5])
endInfo = startAndEnd[1]
endTime = datetime.datetime(endInfo[0], endInfo[1], endInfo[2],
endInfo[3], endInfo[4], endInfo[5])
result.append(StartAndEndTime(startTime, endTime))
return result
def sanityCheckPhaseDates(self):
"""Do some sanity checking on our phase dates."""
#Check phase dates are between end and start times."""
for phaseDate in self.phaseDates:
foundInBetween = False
for startAndEnd in self.startAndEndTimes:
if startAndEnd.isInBetween(phaseDate):
foundInBetween = True
break
if not foundInBetween:
self.notify.error("holiday %d, phaseDate=%s not in between start and end times" %
(self.holidayId, phaseDate))
# check the phase dates are ascending
for index in range( len(self.phaseDates) -1):
if not (self.phaseDates[index] < self.phaseDates[index +1]):
self.notify.error("phaseDate=%s coming before phaseDate=%s" %
(self.phaseDates[index], self.phaseDates[index+1]))
def calcPhase(self, myTime):
"""Return which phase we should be given parameter time.
Will return 0 if it's way before the start time,
and return the last phase if its way after the end time
"""
result = self.getNumPhases()
for index, phaseDate in enumerate( self.phaseDates):
if myTime < phaseDate:
result = index
break
return result
def getNumPhases(self):
"""Return how many phases we have."""
result = len(self.phaseDates) + 1
return result
def isValidStart(self, curStartTime):
"""Print out a message if we're starting at a valid time.
We could start early or later if it's forced by magic words."""
result = False
for startAndEnd in self.startAndEndTimes:
if startAndEnd.isInBetweenInclusive(curStartTime):
result = True
break
def start(self):
"""Start the holiday and set us to the correct phase."""
# start the holiday
# this equivalent to the same bit of code we use in HolidayManagerAI.createHolidays
curTime = datetime.datetime.today()
isValidStart = self.isValidStart(curTime)
if not isValidStart:
self.notify.warning("starting holiday %d at %s but self.startAndEndTimes= %s " %
(self.holidayId, curTime, self.startAndEndTimes))
pass
def stop(self):
pass
def forcePhase(self, newPhase):
self.notify.warning("Child class must defined forcePhase")