Date detection implementation

The code below implements the date detection state machine outlined on the previous page. It is intended as an example of how to approach this rather than a complete solution. Detector style state machines in your own game will likely have different states and transitions according to your needs.

Defining the states

The first thing to do is to define the names of each of the states from the state diagram so they can be used instead of state numbers in the code. This is kept as a define so future versions of the game can include additional states as needed.

Date detection state diagramclasses/fsm/dateDetectFsm.rpy

    # Date detection states.
    #
define dateDetectStates = (
    'notSeen',          # 0
    'dateFirst',        # 1
    'dateSecond',       # 2
    'worried',          # 3
    'saySomething',     # 4
)

Defining the class

This example uses the FsmTimer as a base class so the code can work with state names rather than state numbers and take advantage of its timer functionality. An __init__ method is provided to initialise the base class with the list of states.

Note: There's a syntactical difference between the way the base class is initialised between Ren'Py7/Python2 and Ren'Py8/Python3. Use the tabs below to select the appropriate code for your version.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

init python:

    class DateDetectFsm(FsmTimer):
        """
        State machine for detecting dates.
        """

        # ---------------------------------------------------------------------
        # Constructor
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        def __init__(self):
            """
            Construct a date detecting FSM.
            """
            super(DateDetectFsm, self).__init__('dateDetectStates')   # Python 2


init python:

    class DateDetectFsm(FsmTimer):
        """
        State machine for detecting dates.
        """

        # ---------------------------------------------------------------------
        # Constructor
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        def __init__(self):
            """
            Construct a date detecting FSM.
            """
            super().__init__('dateDetectStates')                      # Python 3

Timer transitions

There are three transitions which trigger when the timer reaches zero. These are handled by overriding the timerTriggered method of FsmTimer. Each reverts to an earlier state, and resets the timer to seven ticks. To clear the timerTrig flag the del operation is used. This could be done whenever the timerTriggered method is called, but that might impact other additions to this state machine.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

        # ---------------------------------------------------------------------
        # Timer methods
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        def timerTriggered(self):
            """
            Back off detection state after timer has elapsed.
            Called when timer reaches zero.
            """
            if self.stateName == 'dateFirst':
                self.stateName = 'notSeen'
                del self.timerTrig
            elif self.stateName == 'dateSecond':
                self.stateName = 'dateFirst'
                self.timer = 7
                del self.timerTrig
            elif self.stateName == 'worried':
                self.stateName = 'dateSecond'
                self.timer = 7
                del self.timerTrig

Inputs

There are three inputs to the state machine: date friend, date other, and said something.

Input: Date Friend

This method is invoked when the NPC is aware of the PC dating her friend. Whenever it transitions it resets the timer to seven ticks.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

        # ---------------------------------------------------------------------
        # Inputs
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        def dateFriend(self):
            """
            PC seen on date with friend.
            """
            if self.stateName == 'notSeen':
                self.stateName = 'dateFirst'
                self.timer = 7
            elif self.stateName == 'dateFirst':
                self.stateName = 'dateSecond'
                self.timer = 7
            elif self.stateName == 'worried':
                self.stateName = 'saySomething'
                self.timer = 7

Input: Date Other

This method is invoked when the NPC is aware of the PC dating someone other than her friend. Whenever it transitions it resets the timer to seven ticks.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

        def dateOther(self):
            """
            PC seen on a date with someone else.
            """
            if self.stateName == 'dateSecond':
                self.stateName = 'worried'
                self.timer = 7

Input: Said Something

This method is invoked when the NPC said something to the player about the way they are treating their friend. Whenever it transitions it resets the timer to seven ticks.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

        def saidSomething(self):
        """
        Said something to the PC.
        """
        if self.stateName == 'saySomething':
            self.stateName = 'dateSecond'
            self.timer = 7

Outputs

There's just one output from this state machine that becomes True when the "correct" sequence of events has occurred.

Output: Say Something

This output is True when the NPC is ready to talk to the player about their behaviour. It would be used to trigger some conditional dialogue between the two. Alternatively it could be used to have the NPC talk to their friend, potentially causing some other change.

classes/fsm/dateDetectFsm.rpy.rpy (cont.)

        # ---------------------------------------------------------------------
        # Outputs
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        def saySomething(self):
            """
            NPC ready to confront PC.
            """
            return self.stateName == 'saySomething'