Difference between revisions of "Summertime Saga API"

From Summertime Saga Wiki
Jump to: navigation, search
m (State specific)
 
(7 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{Message box warning|
+
{{message box warning|The API is depreciated: the definitions are incomplete and will not be updated for future versions. '''USAGE IS HIGHLY DISCOURAGED.'''}}
This API is deprecated. The API is both incomplete and will not be updated for future versions. '''USAGE IS HIGHLY DISCOURAGED'''}}
+
 
{{message box important|
 
* The API for Summertime Saga is currently under development and this page is subject to changes.
 
* This page was last updated as of Summertime Saga version 0.18.0.}}
 
 
The ''Summertime Saga'' development team provides an application programming interface for those who would like to create or modify the game content (modding). In this article you will find documentation on the technical specifications and programming language. Find all the information concerning the modding on [[Modding|its own article]].
 
The ''Summertime Saga'' development team provides an application programming interface for those who would like to create or modify the game content (modding). In this article you will find documentation on the technical specifications and programming language. Find all the information concerning the modding on [[Modding|its own article]].
 
{{TOC right|limit=3}}
 
{{TOC right|limit=3}}
Line 11: Line 8:
 
{{strong|Finite State Machines}} control the flow of the game for a particular character. They are basically linked chains of events (which are called '''States'''). The progress is achieved via a '''Trigger'''.
 
{{strong|Finite State Machines}} control the flow of the game for a particular character. They are basically linked chains of events (which are called '''States'''). The progress is achieved via a '''Trigger'''.
  
[[File:Fsm1.png|thumb|600px|left|A basic example of a FSM]]<br clear=left>
+
[[File:Fsm1.png|thumb|600px|left|A basic example of a FSM]]{{clear|left}}
  
 
=== Creating a FSM ===
 
=== Creating a FSM ===
Line 18: Line 15:
  
 
# Create the states, triggers and a machine instance, providing the necessary arguments to each constructor. Triggers should be created in an init python block of priority 0 or later.
 
# Create the states, triggers and a machine instance, providing the necessary arguments to each constructor. Triggers should be created in an init python block of priority 0 or later.
# Link the states together with {{nowrap|<code>State.add(Trigger t, State next_state)</code>}} method.
+
# Link the states together with {{nowrap|{{code|State.add(Trigger t, State next_state)}}}} method.
# Add all the states to the machine with {{nowrap|<code>Machine.add(*State states)</code>}} method.
+
# Add all the states to the machine with {{nowrap|{{code|Machine.add(*State states)}}}} method.
  
State and machine definition and state linking should be done in a label named <code>character_fsm_init</code>, with <code>character</code> being the character’s name. Additionally, further edits to the machine (such as adding the states) should be done in a label named <code>character_machine_init</code>, with <code>character</code> being the name of the character.
+
State and machine definition and state linking should be done in a label named {{code|character_fsm_init}}, with {{var|character}} being the character’s name. Additionally, further edits to the machine (such as adding the states) should be done in a label named {{code|character_machine_init}}, with {{var|character}} being the name of the character.
  
 
==== Machine specific ====
 
==== Machine specific ====
Line 30: Line 27:
 
; default_loc : A 4x7 matrix of locations to be used as the schedule for where a given machine should be at any point during the week. A 4x1 or 2x4 matrix may be passed as well, and internally will be converted to a 4x7 matrix. If a 1x4 matrix is passed, it’s assumed that the location only varies by time of day, and the day of the week doesn’t matter. A 2x4 matrix will split the two, the first 4-list is for weekdays, and the second for weekend days.
 
; default_loc : A 4x7 matrix of locations to be used as the schedule for where a given machine should be at any point during the week. A 4x1 or 2x4 matrix may be passed as well, and internally will be converted to a 4x7 matrix. If a 1x4 matrix is passed, it’s assumed that the location only varies by time of day, and the day of the week doesn’t matter. A 2x4 matrix will split the two, the first 4-list is for weekdays, and the second for weekend days.
 
; description, states : (to be deprecated) An alternate ways to add states to the machine, and a description that is never used anywhere.
 
; description, states : (to be deprecated) An alternate ways to add states to the machine, and a description that is never used anywhere.
; vars : A dictionary containing variables (as strings, the keys of that dictionary), and init values for those variables. These variables are used with the state actions and the {{nowrap|<code>get(string variable)</code>}} and {{nowrap|<code>set(string variable, object value)</code>}} methods of the machine class. When a specific state doesn’t apply, or you need a variable to change based on the progress with a given path. The {{nowrap|<code>sex speed</code>}} variable is assumed to be used only for setting the speed of the animation for sex scenes.
+
; vars : A dictionary containing variables (as strings, the keys of that dictionary), and init values for those variables. These variables are used with the state actions and the {{nowrap|{{code|get(string variable)}}}} and {{nowrap|{{code|set(string variable, object value)}}}} methods of the machine class. When a specific state doesn’t apply, or you need a variable to change based on the progress with a given path. The {{nowrap|{{code|sex speed}}}} variable is assumed to be used only for setting the speed of the animation for sex scenes.
  
 
The machine class defines a lot of useful methods:
 
The machine class defines a lot of useful methods:
  
; set_priority(int priority) : Set the priority of the machine. Currently used to display the hints on the phone. A machine with a <code>priority</code> of 1 or more will show up in the phone. This may be used later to sort quests into main quests and side quests.
+
; set_priority(int priority) : Set the priority of the machine. Currently used to display the hints on the phone. A machine with a {{var|priority}} of 1 or more will show up in the phone. This may be used later to sort quests into main quests and side quests.
; property button_dialogue : Return the button dialogue for that machine ({{nowrap|<code>machine name</code>}} + <code>_button_dialogue</code>), used in the [[#TalkTo|TalkTo]] screen action.
+
; property button_dialogue : Return the button dialogue for that machine ({{nowrap|{{code|machine name}}}} + {{code|_button_dialogue}}), used in the [[#TalkTo|TalkTo]] screen action.
 
; image, show, say, and hide : Now defunct methods.
 
; image, show, say, and hide : Now defunct methods.
 
; property progress : Return an integer that is the progress out of a 100%.
 
; property progress : Return an integer that is the progress out of a 100%.
 
; add_action(*actions) : Add a machine action to this machine.
 
; add_action(*actions) : Add a machine action to this machine.
; set_state(State state, bool null_delay=False) : Revert the machine to init, then advance the machine until it is in the state <code>state</code>. If <code>null_delay</code> is <code>True</code>, set the delay of the state reached to 0.
+
; set_state(State state, bool null_delay=False) : Revert the machine to init, then advance the machine until it is in the state {{var|state}}. If {{code|null_delay}} is {{code|True}}, set the delay of the state reached to 0.
 
; machine_trigger : Trigger for machine actions.
 
; machine_trigger : Trigger for machine actions.
; trigger(Trigger trigger, bool noactions=False) : Trigger the machine to pass onto the next state. If <code>noactions</code> is <code>True</code>, don’t process the actions.
+
; trigger(Trigger trigger, bool noactions=False) : Trigger the machine to pass onto the next state. If {{code|noactions}} is {{code|True}}, don’t process the actions.
 
; add(State* states) : Add the states to the machine.
 
; add(State* states) : Add the states to the machine.
; get(string var) : Get the machine variable <code>var</code>.
+
; get(string var) : Get the machine variable {{var|var}}.
; set(string var, object value) : Set the machine variable <code>var</code>.
+
; set(string var, object value) : Set the machine variable {{var|var}}.
 
; get_state() : Return the current state the machine is in.
 
; get_state() : Return the current state the machine is in.
 
; property where : Compute where the machine should be based on her default location, time of day, day of the week, forced location and if it’s forced or not for that time of day. Return a location.
 
; property where : Compute where the machine should be based on her default location, time of day, day of the week, forced location and if it’s forced or not for that time of day. Return a location.
 
; is_state(State* states) : Return boolean if the machine is in any of the provided states.
 
; is_state(State* states) : Return boolean if the machine is in any of the provided states.
; between_states(State stateBegin, State* statesEnd) : Return boolean if the machine is between <code>stateBegin</code> and any of the <code>statesEnd</code> (non‐inclusive).
+
; between_states(State stateBegin, State* statesEnd) : Return boolean if the machine is between {{var|stateBegin}} and any of the {{var|statesEnd}} (non‐inclusive).
 
; finished_state(State* states) : Return boolean if the machine has finished any of the states provided.
 
; finished_state(State* states) : Return boolean if the machine has finished any of the states provided.
  
 
==== State specific ====
 
==== State specific ====
  
You may want to add a delay to state creation by using the delay optional argument. That delay is the number of days until that state is effectively reached once the trigger has been "pulled".
+
You may want to add a delay to state creation by using the delay optional argument. That delay is the number of days until that state is effectively reached once the trigger has been “pulled”.
  
 
When linking states together, you may want to add '''state actions''' to be executed when the state is triggered. Those actions are:
 
When linking states together, you may want to add '''state actions''' to be executed when the state is triggered. Those actions are:
  
; ['set','flag_1'] : Set the value of <code>flag_1</code> to <code>True</code>.
+
; ['set','flag_1'] : Set the value of {{var|flag_1}} to {{code|True}}.
; ['clear','flag_1'] : Set the value of <code>flag_1</code> to <code>False</code>.
+
; ['clear','flag_1'] : Set the value of {{var|flag_1}} to {{code|False}}.
; ['toggle','flag_1'] : Toggle the value of <code>flag_1</code> between <code>True</code> and <code>False</code>.
+
; ['toggle','flag_1'] : Toggle the value of {{var|flag_1}} between {{code|True</code> and {{code|False}}.
; ['assign',['v1',100]] : Set the value of <code>v1</code> to 100.
+
; ['assign',['v1',100]] : Set the value of {{var|v1}} to 100.
; ['inc','v1'] : Increase the value of <code>v1</code> by 1.
+
; ['inc','v1'] : Increase the value of {{var|v1}} by 1.
; ['dec','v1'] : Decrease the value of <code>v1</code> by 1.
+
; ['dec','v1'] : Decrease the value of {{var|v1}} by 1.
; <nowiki>['triggeronzero':['v1',T_a_trigger]]</nowiki> : Set <code>v1 -= 1</code>, and fire the trigger <code>T_a_trigger</code> if <code>v1</code> is less than or equal to 0.
+
; <nowiki>['triggeronzero':['v1',T_a_trigger]]</nowiki> : Set {{code|v1 -= 1}}, and fire the trigger {{code|T_a_trigger}} if {{var|v1}} is less than or equal to 0.
; ['trigger',T_a_trigger] : Fire the trigger <code>T_a_trigger</code>.
+
; ['trigger',T_a_trigger] : Fire the trigger {{code|T_a_trigger}}.
 
; ['call','label'] : Make a Ren’Py call to label. Label MUST return.
 
; ['call','label'] : Make a Ren’Py call to label. Label MUST return.
 
; <nowiki>['location', [machine, {"tod":tod, "place":place}]]</nowiki> : Set the forced location for the machine to place (moves the non‐player character).
 
; <nowiki>['location', [machine, {"tod":tod, "place":place}]]</nowiki> : Set the forced location for the machine to place (moves the non‐player character).
: <code>tod</code> is 1‐indexed (<code>1</code> is morning, <code>2</code> is afternoon, <code>3</code> is evening, <code>4</code> is night).
+
: {{var|tod}} is 1‐indexed ({{code|1}} is morning, {{code|2}} is afternoon, {{code|3}} is evening, {{code|4}} is night).
 
; <nowiki>['force', [machine, {"tod": list or int, "flag": 4-list or bool}]]</nowiki> : Say if the location is forced at tod or sets force flags according to the 4-list provided.
 
; <nowiki>['force', [machine, {"tod": list or int, "flag": 4-list or bool}]]</nowiki> : Say if the location is forced at tod or sets force flags according to the 4-list provided.
 
; ['unforce', None/machine] : Reset the locations for machine or the machine specified forced.
 
; ['unforce', None/machine] : Reset the locations for machine or the machine specified forced.
 
; ['exec', callable] : Call the callable function or method.
 
; ['exec', callable] : Call the callable function or method.
; ['exec', [callable, *args]] : Call the callable function or method and pass in the arguments <code>args</code> specified.
+
; ['exec', [callable, *args]] : Call the callable function or method and pass in the arguments {{code|args}} specified.
; ['condition', [condition_string, actions_list_true, actions_list_false, (optional) machine]] : Execute the actions in <code>actions_list_true</code> if <code>condition_string</code> evaluates to <code>True</code>, otherwise executes <code>actions_list_false</code>. Conditions lists are assumed to be state actions.
+
; ['condition', [condition_string, actions_list_true, actions_list_false, (optional) machine]] : Execute the actions in {{code|actions_list_true}} if {{code|condition_string}} evaluates to {{code|True}}, otherwise executes {{code|actions_list_false}}. Conditions lists are assumed to be state actions.
 
; ['action', [target_machine, action, target]] : Execute the state action on another machine.
 
; ['action', [target_machine, action, target]] : Execute the state action on another machine.
 
; <nowiki>['setdefaultloc', [[Location, Location, Location, Location]]]</nowiki> : Set the default locations for the current machine.
 
; <nowiki>['setdefaultloc', [[Location, Location, Location, Location]]]</nowiki> : Set the default locations for the current machine.
: The argument is in the same format as the <code>default_loc</code> argument in the machine constructor (1x4, 2x4 or 7x4 matrices).
+
: The argument is in the same format as the {{code|default_loc}} argument in the machine constructor (1x4, 2x4 or 7x4 matrices).
; ['setoutfit', [location, outfit]] : Set the outfit for that location. <code>outfit</code> may be either a string, or a 1x4, 2x4, 7x4 array of strings (similar to the locations).
+
; ['setoutfit', [location, outfit]] : Set the outfit for that location. {{var|outfit}} may be either a string, or a 1x4, 2x4, 7x4 array of strings (similar to the locations).
; ['setnaked', True/False] : Set <code>is_naked</code> attribute of the [[#Outfit manager|outfit manager]] to <code>True</code> or <code>False</code> for the current machine.
+
; ['setnaked', True/False] : Set {{code|is_naked}} attribute of the {{section link||Outfit manager}} to {{code|True}} or {{code|False}} for the current machine.
 
; <nowiki>['setdefaultoutfit', [outfit, {'tod':tod, 'dow':dow}]]</nowiki> : Set the default outfit for the current machine.
 
; <nowiki>['setdefaultoutfit', [outfit, {'tod':tod, 'dow':dow}]]</nowiki> : Set the default outfit for the current machine.
: <code>tod</code> and <code>dow</code> can be omitted. <code>outfit</code> is a required argument in the format of a string or a 1x4, 2x4, 7x4 matrix.
+
: {{var|tod}} and {{var|dow}} can be omitted. {{var|outfit}} is a required argument in the format of a string or a 1x4, 2x4, 7x4 matrix.
: If <code>tod</code> and <code>dow</code> are omitted, <code>outfit</code> cannot be a matrix, but only a string. A workaround solution exists by passing the {{nowrap|<code>{"tod":None,"dow":None}</code>}} dictionary.
+
: If {{var|tod}} and {{var|dow}} are omitted, {{var|outfit}} cannot be a matrix, but only a string. A workaround solution exists by passing the {{nowrap|{{code|{"tod":None,"dow":None}}}}} dictionary.
  
 
=== Editing an FSM ===
 
=== Editing an FSM ===
  
Editing an existing Finite State Machine is as simple as calling the <code>clear()</code> method of the state you want to clear. It takes one argument, <code>cleardelay</code>, which defaults to <code>False</code>. If <code>cleardelay</code> is <code>True</code>, then the delay of the state is cleared. If you don’t want to edit the state but clear the delay, just set the <code>State.delay</code> attribute to 0 instead.
+
Editing an existing Finite State Machine is as simple as calling the {{code|clear()}} method of the state you want to clear. It takes one argument, {{code|cleardelay}}, which defaults to {{code|False}}. If {{code|cleardelay}} is {{code|True}}, then the delay of the state is cleared. If you don’t want to edit the state but clear the delay, just set the {{code|State.delay}} attribute to 0 instead.
  
 
Once cleared, the state is unlinked to the FSM, which means that story will block at that state. It’s up to you to link that state to the rest of the machine, with additional states in the middle for your code.
 
Once cleared, the state is unlinked to the FSM, which means that story will block at that state. It’s up to you to link that state to the rest of the machine, with additional states in the middle for your code.
Line 90: Line 87:
 
=== Using FSMs ===
 
=== Using FSMs ===
  
Using FSMs is easy. In the location labels, the machine state should be tested with <code>is_state(State* states)</code> method. Multiple states may be passed in that method, as well as a list of states. If the machine is in any of the provided states, then the method returns <code>True</code>, and <code>False</code> otherwise. For convenience, similar methods are used: <code>between_states(State state1, State state2)</code> returns <code>True</code> if the machine is in any state between <code>state1</code> and <code>state2</code> but is not in state <code>state2</code>; {{nowrap|<code>finished_state(State* states)</code>}} returns <code>True</code> if the machine has finished any of the provided states.
+
Using FSMs is easy. In the location labels, the machine state should be tested with {{code|is_state(State* states)}} method. Multiple states may be passed in that method, as well as a list of states. If the machine is in any of the provided states, then the method returns {{code|True}}, and {{code|False}} otherwise. For convenience, similar methods are used: {{code|between_states(State state1, State state2)}} returns {{code|True}} if the machine is in any state between {{var|state1}} and {{var|state2}} but is not in state {{var|state2}}; {{nowrap|{{code|finished_state(State* states)}}}} returns {{code|True}} if the machine has finished any of the provided states.
  
To advance to the next state, {{nowrap|<code>trigger(Trigger t)</code>}} method of the machine class is used. It moves the machine to the next state associated with that trigger if the provided trigger is in the current state table, and doesn’t do anything otherwise.
+
To advance to the next state, {{nowrap|{{code|trigger(Trigger t)}}}} method of the machine class is used. It moves the machine to the next state associated with that trigger if the provided trigger is in the current state table, and doesn’t do anything otherwise.
  
 
=== Example of FSM ===
 
=== Example of FSM ===
Line 136: Line 133:
 
On starting the game, and on loading a save file or reloading the game, the states are created and linked together, but after the machine itself is initialized. Here is the order applies to the previous example:
 
On starting the game, and on loading a save file or reloading the game, the states are created and linked together, but after the machine itself is initialized. Here is the order applies to the previous example:
  
: '''init (triggers)''' > '''call (after_load/start)''' > '''call <code>cassie_machine_init</code>''' > '''call <code>cassie_fsm_init</code>'''
+
: '''init (triggers)''' > '''call (after_load/start)''' > '''call {{code|cassie_machine_init}}''' > '''call {{code|cassie_fsm_init}}'''
  
Explication: the machine is named <code>M_cassie</code> and is given the name <code>cassie</code>. This name is used in several places throughout the code to refer to that object. Furthermore, Cassie’s default location (<code>default_loc</code> argument) specifies that the character is at the pool from morning to evening, and nowhere at night. A dictionary is initialized as well to keep track of some other variables, like the sex animations speed, the previous sexual interactions with that character, the variables to trigger repeatable dialogues, counters, etc.<br>
+
Explication: the machine is named {{code|M_cassie}} and is given the name {{code|cassie}}. This name is used in several places throughout the code to refer to that object. Furthermore, Cassie’s default location ({{code|default_loc}} argument) specifies that the character is at the pool from morning to evening, and nowhere at night. A dictionary is initialized as well to keep track of some other variables, like the sex animations speed, the previous sexual interactions with that character, the variables to trigger repeatable dialogues, counters, etc.<br>
 
In the same label, you can set up the [[#Outfit manager|outfit system]] of that character, as seen in the next section, or add machine actions to the FSM. This is also the place to set the priority of the Finite State Machine.
 
In the same label, you can set up the [[#Outfit manager|outfit system]] of that character, as seen in the next section, or add machine actions to the FSM. This is also the place to set the priority of the Finite State Machine.
  
The states are created and linked in the label <code>cassie_fsm_init</code>. In the given example, the last link has a state action attached to it, which means that on reaching the <code>S_cassie_end</code> state, the machine automatically calls the unlock method for the '''A_drowning_in_pussy''' achievement. In the same way, other state actions may be defined.
+
The states are created and linked in the label {{code|cassie_fsm_init}}. In the given example, the last link has a state action attached to it, which means that on reaching the {{code|S_cassie_end}} state, the machine automatically calls the unlock method for the '''A_drowning_in_pussy''' achievement. In the same way, other state actions may be defined.
  
 
<u>'''More examples:'''</u>
 
<u>'''More examples:'''</u>
Line 174: Line 171:
 
=== Outfit manager ===
 
=== Outfit manager ===
  
Every FSM has an {{strong|outfit manager}} instance attached to it. This system is designed a bit like the Location system: each outfit has a specific time of day/day of week schedule for which the character is wearing that outfit. The manager also considers the current location and handles whether the character is naked or not, with the <code>is_naked</code> attribute.
+
Every FSM has an {{strong|outfit manager}} instance attached to it. This system is designed a bit like the Location system: each outfit has a specific time of day/day of week schedule for which the character is wearing that outfit. The manager also considers the current location and handles whether the character is naked or not, with the {{code|is_naked}} attribute.
  
To bind the schedule of an outfit to a specific location, use the method <code>bind_outfit_to_location</code>, with the location as first argument, and the outfit string/schedule as second argument.
+
To bind the schedule of an outfit to a specific location, use the method {{code|bind_outfit_to_location}}, with the location as first argument, and the outfit string or schedule as second argument.
  
 
An outfit schedule is very similar to a location schedule, it’s a 1x4/2x4/7x4 matrix of strings, each of those strings being the outfit of a given pair of time of day/day of week.
 
An outfit schedule is very similar to a location schedule, it’s a 1x4/2x4/7x4 matrix of strings, each of those strings being the outfit of a given pair of time of day/day of week.
Line 182: Line 179:
 
'''Init arguments:'''
 
'''Init arguments:'''
  
; default_outfit : Defaults to <code><nowiki>[["dressed", "dressed", "dressed", "dressed"]]</nowiki></code>.
+
; default_outfit : Defaults to {{code|<nowiki>[["dressed", "dressed", "dressed", "dressed"]]</nowiki>}}.
; is_naked : Defaults to <code>False</code>.
+
; is_naked : Defaults to {{code|False}}.
  
 
'''Methods and attributes:'''
 
'''Methods and attributes:'''
Line 193: Line 190:
 
; property get() : Return the proper outfit given the current time of day, day of week, whether the machine is naked or not, and current location.
 
; property get() : Return the proper outfit given the current time of day, day of week, whether the machine is naked or not, and current location.
  
{{message box important|Below is a copy‐paste from Discord that will be rewritten later. It gets the point across though.}}
+
{{message box important|Below is a copy‐paste from Discord. It gets the point across though.}}
  
So basically how the new outfit system works; you have to setup the outfit manager. The same manager has one method called <code>bind_outfit_to_location</code> that takes 2 arguments: <code>location</code>, a location object, and <code>outfit</code>, which can be an outfit string or an outfit schedule, i.e. a matrix just like the locations. This method creates a map of outfits to use in certain conditions.
+
So basically how the new outfit system works; you have to setup the outfit manager. The same manager has one method called {{code|bind_outfit_to_location}} that takes 2 arguments: {{var|location}}, a location object, and {{var|outfit}}, which can be an outfit string or an outfit schedule, i.e. a matrix just like the locations. This method creates a map of outfits to use in certain conditions.
  
Moreover, the outfits can be set with a simple state action which takes care of calling the <code>bind_outfit_to_location</code> method. Now, to get the outfit, it’s as simple as calling <code>M_diane.outfit.get</code> (without any parentheses as <code>get</code> is a property of the outfit manager). The variable <code>is_naked</code> has also been moved to the outfit manager.
+
Moreover, the outfits can be set with a simple state action which takes care of calling the {{code|bind_outfit_to_location}} method. Now, to get the outfit, it’s as simple as calling {{code|M_diane.outfit.get}} (without any parentheses as {{code|get}} is a property of the outfit manager). The variable {{var|is_naked}} has also been moved to the outfit manager.
  
Instead of <code>M_diane.get_naked_str</code>, use <code>M_diane.outfit.get</code>; and instead of doing <code>M_diane.outfit = "whatever"</code>, configure the outfit manager with <code>M_diane.outfit.bind_outfit_to_location(L_home_livingroom, "casual")</code>. If you want more granularity, write <code>M_diane.outfit.bind_outfit_to_location(L_home_livingroom, <nowiki>[["dressed", "dressed", "casual", "casual"]]</nowiki>)</code>. You can even set that matrix to bean individual outfit for each time of day, each day of the week.
+
Instead of {{code|M_diane.get_naked_str}}, use {{code|M_diane.outfit.get}}; and instead of doing {{code|M_diane.outfit = "{{var|whatever}}"}}, configure the outfit manager with {{code|M_diane.outfit.bind_outfit_to_location(L_home_livingroom, "casual")}}. If you want more granularity, write {{code|M_diane.outfit.bind_outfit_to_location(L_home_livingroom, <nowiki>[["dressed", "dressed", "casual", "casual"]]</nowiki>)}}. You can even set that matrix to bean individual outfit for each time of day, each day of the week.
  
<code>setnaked</code> and <code>setoutfit</code> are dedicated to the state action:
+
{{code|setnaked}} and {{code|setoutfit}} are dedicated to the state action:
* <code>setnaked</code> sets the machine to be naked or not, thing you can also do with <code>M_diane.outfit.is_naked = True/False</code> (<code>True</code> or <code>False</code>).
+
* {{code|setnaked}} sets the machine to be naked or not, thing you can also do with {{code|M_diane.outfit.is_naked = True/False}} ({{code|True}} or {{code|False}}).
* <code>setoutfit</code> calls <code>bind_outfit_to_location</code> method with two arguments specified like that: <code>actions=["setoutfit", [L_home_livingroom, "casual"]]</code>
+
* {{code|setoutfit}} calls {{code|bind_outfit_to_location}} method with two arguments specified like that: {{code|actions=["setoutfit", [L_home_livingroom, "casual"]]}}
  
<u>'''Example for</u> <code>Machine.outfit.get property</code>:'''
+
<u>'''Example for</u> {{code|Machine.outfit.get property}}:'''
  
 
<syntaxhighlight lang="Python">
 
<syntaxhighlight lang="Python">
Line 218: Line 215:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Use <code>del M_diane.outfit.outfits[L_home_livingroom]</code> to revert back to the default if needed.
+
Use {{nowrap|{{code|del M_diane.outfit.outfits[L_home_livingroom]}}}} to revert back to the default if needed.
  
 
=== Pregnancy manager ===
 
=== Pregnancy manager ===
  
 
=== Dating System ===
 
=== Dating System ===
In version 0.19 of the game, the dating system was introduced, as a way to limit the impact it would have on future save and compatibility.
 
  
The dating system functions through <code>dating_points</code>. Dating points are a secondary currency that is specific to each machine, and that you earn by performing tasks with said machines.
+
The dating system was introduced, as a way to limit the impact it would have on future save and compatibility. It functions through {{code|dating_points}}. Dating points are a secondary currency that is specific to each machine, and that you earn by performing tasks with said machines.
  
 
==== The dating.json file ====
 
==== The dating.json file ====
Each datable state machine has an entry in that file, which specifies what are this machine’s likes and dislikes, among other things. Example :
+
 
 +
Each datable state machine has an entry in that file, which specifies what are this machine’s likes and dislikes, among other things. Example:
  
 
<syntaxhighlight lang=js>
 
<syntaxhighlight lang=js>
Line 261: Line 258:
 
==== Dating points exponential decrease ====
 
==== Dating points exponential decrease ====
  
The more one performs a specific action with a machine, the less points are awarded for performing said action. The formula is as follows :
+
The more one performs a specific action with a machine, the less points are awarded for performing said action. The formula is as follows:
  
<code>value_awarded = base_value * exp(-0.3 * t), t= n of times the action was performed.</code>
+
{{nowrap|{{code|value_awarded = base_value * exp(-0.3 * t)}}}}, {{var|t}} = number of times the action was performed.
  
 
The result is then rounded, and cast as an integer before being added to the total dating points.
 
The result is then rounded, and cast as an integer before being added to the total dating points.
Line 269: Line 266:
 
== Locations ==
 
== Locations ==
  
{{strong|Locations}} handle all the locations in the game. They are represented as a tree, with the possibility of multiple parents (like Smith’s Bedroom, which has the front yard and the hallway as its parents). A required attribute is the location name.
+
{{strong|Locations}} handle all the locations in the game. They are represented as a tree, with the possibility of multiple parents (like Mrs. Smith’s bedroom, which has the front yard and the hallway as its parents). A required attribute is the location name.
  
 
=== Creating a location ===
 
=== Creating a location ===
  
Locations are easy to create. It’s as simple as instantiating the Location class, and providing a name for that location to the constructor (which is the only required argument). Locations are mutable, so they should be instantiated in a <code>modname_locations_init</code> label.
+
Locations are easy to create. It’s as simple as instantiating the Location class, and providing a name for that location to the constructor (which is the only required argument). Locations are mutable, so they should be instantiated in a {{code|modname_locations_init}} label.
  
 
Furthermore, a locations has several optional arguments:
 
Furthermore, a locations has several optional arguments:
Line 280: Line 277:
 
; unlock_popup : The name of a Ren’Py‐defined displayable for the popup that should show up when the location is unlocked.
 
; unlock_popup : The name of a Ren’Py‐defined displayable for the popup that should show up when the location is unlocked.
 
; background : The name of the background used for that location. The background name must follow several conventions to properly work:
 
; background : The name of the background used for that location. The background name must follow several conventions to properly work:
:* Must be in the folder <code>images/backgrounds/</code>
+
:* Must be in the folder {{code|images/backgrounds/}}
:* Name must start with <code>location_</code>
+
:* Name must start with {{code|location_}}
:* Name must end with <code>_day.jpg</code>, <code>_evening.jpg</code> or <code>_night.jpg</code>, respectively for day, evening or night background.
+
:* Name must end with {{code|_day.jpg}}, {{code|_evening.jpg}} or {{code|_night.jpg}}, respectively for day, evening or night backgrounds.
:* For Halloween or Christmas backgrounds, the strings <code>_halloween</code> and <code>_christmas</code> must be added before the time of day code. This will allow the game to find relevant backgrounds for the time period.
+
:* For Halloween or Christmas backgrounds, the strings {{code|_halloween}} and {{code|_christmas}} must be added before the time of day code. This will allow the game to find relevant backgrounds for the time period.
: Whatever is leftover should be provided in the background argument of the constructor. The full file path will be constructed by the background (resp. <code>background_blur</code>) properties.
+
: Whatever is leftover should be provided in the background argument of the constructor. The full file path will be constructed by the background (respectively {{code|background_blur}}) properties.
; parents : Either a list of location or a single location instance. Provide the parents of that location. If left empty, the location is assumed to be the root of the tree. The root should always be <code>L_map</code> (or <code>L_NULL</code>, but <code>L_NULL</code> should not have children).
+
; parents : Either a list of location or a single location instance. Provide the parents of that location. If left empty, the location is assumed to be the root of the tree. The root should always be {{code|L_map}} (or {{code|L_NULL}}, but {{code|L_NULL}} should not have children).
; locked : Defaults to <code>False</code>. Whether that location should be initially locked, and inaccessible at the beginning of the game.
+
; locked : Defaults to {{code|False}}. Whether that location should be initially locked, and inaccessible at the beginning of the game.
; label : (to be deprecated) The label name for that location. <code>formatted_name</code> property will be used later on.
+
; label : (to be deprecated) The label name for that location. {{code|formatted_name}} property will be used later on.
  
 
<u>'''Example:'''</u>
 
<u>'''Example:'''</u>
Line 293: Line 290:
 
<syntaxhighlight lang="Python">L_diane_barn = Location("Diane's Barn", unlock_popup="popup_diane_barn", background="barn_frontyard", parents=L_map, locked=True)</syntaxhighlight>
 
<syntaxhighlight lang="Python">L_diane_barn = Location("Diane's Barn", unlock_popup="popup_diane_barn", background="barn_frontyard", parents=L_map, locked=True)</syntaxhighlight>
  
This code defines a location named "Diane's Barn" (formatted name: <code>dianes_barn</code>), child of <code>L_map</code>, that shows <code>popup_diane_barn</code> on unlock, and that is initially locked. The background is set as <code>barn_frontyard</code>, which means the game looks for files named <code>backgrounds/location_barn_frontyard_day.jpg</code> or <code>backgrounds/location_barn_frontyard_night.jpg</code> for instance.
+
This code defines a location named "Diane's Barn" (formatted name: {{code|dianes_barn}}), child of {{code|L_map}}, that shows {{code|popup_diane_barn}} on unlock, and that is initially locked. The background is set as {{var|barn_frontyard}}, which means the game looks for files named {{code|backgrounds/location_barn_frontyard_day.jpg}} or {{code|backgrounds/location_barn_frontyard_night.jpg}} for instance.
  
 
== User‐defined screen actions ==
 
== User‐defined screen actions ==
Line 305: Line 302:
 
=== BuyItem ===
 
=== BuyItem ===
  
This action expects an item, or item identifier string to be passed to it. When activated, buys the item, and shows the appropriate failing popups should the transaction fail. Optionally, you may add a callback label with the <code>callback_label</code> optional argument. If the transaction succeeds, the game then calls that label. Useful if you want some story element to trigger after buying an item.
+
This action expects an item, or item identifier string to be passed to it. When activated, buys the item, and shows the appropriate failing popups should the transaction fail. Optionally, you may add a callback label with the {{code|callback_label}} optional argument. If the transaction succeeds, the game then calls that label. Useful if you want some story element to trigger after buying an item.
  
 
=== TalkTo ===
 
=== TalkTo ===
  
This action is used to talk to the character. It hides the screen and start the conversation with the given character. It expects a machine instance, or a string to identify it, in which case it attempts to find the machine in the <code>store.machines</code> dictionary.
+
This action is used to talk to the character. It hides the screen and start the conversation with the given character. It expects a machine instance, or a string to identify it, in which case it attempts to find the machine in the {{code|store.machines}} dictionary.
  
 
=== ClearPersistent ===
 
=== ClearPersistent ===
  
This action resets all persistent data (cookie jar, time spent playing, achievements etc). No arguments expected.
+
This action resets all persistent data (cookie jar, time spent playing, achievements, etc.). No arguments expected.
  
 
=== GetItem ===
 
=== GetItem ===
Line 323: Line 320:
 
=== Items and inventory management ===
 
=== Items and inventory management ===
  
The {{strong|inventory manager}} is fully automatic, but you may want to add some items to the game. Items are stored in <code>items.json</code> file located in <code>scripts/data/folder</code>.
+
The {{strong|inventory manager}} is fully automatic, but you may want to add some items to the game. Items are stored in {{code|items.json}} file located in {{code|scripts/data/folder}}.
  
 
<u>'''Example:'''</u>
 
<u>'''Example:'''</u>
Line 341: Line 338:
  
 
In order:
 
In order:
* <code>birth_control_pills</code>: the item identifier.
+
* birth_control_pills: the item identifier.
* <code>id</code>: a numeric id. Unused at this moment.
+
* id: a numeric id. Unused at this moment.
* <code>name</code>: the item name, as seen in the inventory.
+
* name: the item name, as seen in the inventory.
* <code>cost</code>: the item cost. It’s cast as an integer, and prevents the player from picking up the item if he doesn’t have sufficient money.
+
* cost: the item cost. It’s cast as an integer, and prevents the player from picking up the item if he doesn’t have sufficient money.
* <code>image</code>: the item miniature image, as seen in the inventory.
+
* image: the item miniature image, as seen in the inventory.
* <code>description</code>: the item description, as seen in the inventory.
+
* description: the item description, as seen in the inventory.
* <code>closeup</code>: the item close‐up image, if there is one.
+
* closeup: the item close‐up image, if there is one.
* <code>dialogue_label</code>: the item dialogue label. It’s triggered if it exists, on clicking the item. The label should always return, and take an item argument.
+
* dialogue_label: the item dialogue label. It’s triggered if it exists, on clicking the item. The label should always return, and take an item argument.
* <code>popup_image</code>: the item popup image. It’s displayed when first acquiring the item, leave the string empty if there is none.
+
* popup_image: the item popup image. It’s displayed when first acquiring the item, leave the string empty if there is none.
  
 
=== Text messages ===
 
=== Text messages ===
  
{{strong|Text messages}} are stored in this format in <code>text_messages.json</code> file. Once defined there, you can use <code>player.receive_message(message_id)</code> for the player to receive the message.
+
{{strong|Text messages}} are stored in this format in {{code|text_messages.json}} file. Once defined there, you can use {{code|player.receive_message(message_id)}} for the player to receive the message.
  
 
<u>'''Example:'''</u>
 
<u>'''Example:'''</u>
Line 367: Line 364:
  
 
In order:
 
In order:
* <code>mia02</code>: the message identifier. Must be unique. Message identifiers ending in <code>_pregnancy</code> or <code>_pregnancy_labor</code> are reserved for the pregnancy system messages.
+
* mia02: the message identifier. Must be unique. Message identifiers ending in {{code|_pregnancy}} or {{code|_pregnancy_labor}} are reserved for the pregnancy system messages.
* <code>sender</code>: the name of the machine that sent this message.
+
* sender: the name of the machine that sent this message.
* <code>content_preview</code>: the preview text displayed on the phone main screen.
+
* content_preview: the preview text displayed on the phone main screen.
* <code>content</code>: the actual message displayed when clicking the message in the phone.
+
* content: the actual message displayed when clicking the message in the phone.
* <code>image</code>: the image used as a "profile picture on the phone.
+
* image: the image used as a profile picture on the phone.
  
 
=== Achievements ===
 
=== Achievements ===
  
{{strong|Achievements}} are defined in <code>achievements.json</code> file.
+
{{strong|Achievements}} are defined in {{code|achievements.json}} file.
  
 
<u>'''Example:'''</u>
 
<u>'''Example:'''</u>
Line 391: Line 388:
  
 
In order:
 
In order:
* <code>angler</code>: the achievement identifier. Must be unique. The identifier is used to name the achievement variable. Dashes are replaced with underscores. All achievements are prefixed with "A_".
+
* angler: the achievement identifier. Must be unique. The identifier is used to name the achievement variable. Dashes are replaced with underscores. All achievements are prefixed with “A_”.
* <code>id</code>: a numeric id. Unused.
+
* id: a numeric id. Unused.
* <code>name</code>: the achievement’s name.
+
* name: the achievement’s name.
* <code>description</code>: the achievement’s description. New lines are inserted every 30 characters, words are not split in the middle.
+
* description: the achievement’s description. New lines are inserted every 30 characters, words are not split in the middle.
* <code>hidden</code>: whether it’s a secret achievement or not.
+
* hidden: whether it’s a secret achievement or not.
* <code>enabled</code>: if the achievement can be achieved.
+
* enabled: if the achievement can be achieved.
* <code>image</code>: the icon for this achievement.
+
* image: the icon for this achievement.
  
 
=== Dialogues ===
 
=== Dialogues ===
  
The <code>dialogues.json</code> file is used for the dialogue lines written by the higher tier contributors of DarkCookie’s Patreon.
+
The {{code|dialogues.json}} file is used for the dialogue lines written by the higher tier contributors of DarkCookie’s Patreon.
 
 
=== Key binding ===
 
 
 
{{message box warning|This code is not yet implemented.}}
 
 
 
This file lists the default key bindings used by the minigames.
 
 
 
=== Furnishings ===
 
 
 
{{message box warning|This code is not yet implemented.}}
 
 
 
This file will be similar to <code>items.json</code>. At the moment, no furnishings have been implemented. They will be available for the [[beachhouse]] location with an upgrade system.
 
  
 
== Game manager class ==
 
== Game manager class ==
Line 419: Line 404:
 
The {{strong|game manager class}} handles everything that is related to the gameplay itself, but is not planned to be extendable by mods. Its methods and attributes are still usable and should be used for your mod.
 
The {{strong|game manager class}} handles everything that is related to the gameplay itself, but is not planned to be extendable by mods. Its methods and attributes are still usable and should be used for your mod.
  
; language : Set the language of the game which is used for translations. Defaults to <code>"en"</code> for English with other languages in the game untranslated.
+
; language : Set the language of the game which is used for translations. Defaults to {{var|"en"}} for English with other languages in the game untranslated.
; cheat_mode : Return <code>True</code> if the game is currently in cheat mode (thus allowing minigames to be skipped).
+
; cheat_mode : Return {{code|True}} if the game is currently in cheat mode (thus allowing minigames to be skipped).
; CA_FILE : Path to a certification file used to enable requests to websites. Used only if the player has checked the option "Allow Internet Access".
+
; CA_FILE : Path to a certification file used to enable requests to websites. Used only if the player has checked the option “Allow Internet Access”.
 
; lock_ui() : Lock the user interface.
 
; lock_ui() : Lock the user interface.
 
; unlock_ui() : Unlock the user interface.
 
; unlock_ui() : Unlock the user interface.
Line 427: Line 412:
 
; dialog_select(string label_name) : Classmethod that chooses a label based on its name and the language class attribute. To be used to split dialogues and logic, and also allows your mod to be easily translated.
 
; dialog_select(string label_name) : Classmethod that chooses a label based on its name and the language class attribute. To be used to split dialogues and logic, and also allows your mod to be easily translated.
 
; choose_label(string template) : Classmethod that chooses a label at random that matches the template passed in the arguments.
 
; choose_label(string template) : Classmethod that chooses a label at random that matches the template passed in the arguments.
; main() : Method that is to be called at the end of every label the player jumps to (and not '''called''' to). It calls the player’s location screen, and checks for achievements and other stuff. This would be the only method expandable upon. It also clears the return stack, so that traceback are not indigestible. It may take two arguments, <code>clear_return_stack</code> to enable or not return stack clearing, and location, to call the screen of another location than the player’s currently in.
+
; main() : Method that is to be called at the end of every label the player jumps to (and not '''called''' to). It calls the player’s location screen, and checks for achievements and other stuff. This would be the only method expandable upon. It also clears the return stack, so that traceback are not indigestible. It may take two arguments, {{code|clear_return_stack}} to enable or not return stack clearing, and location, to call the screen of another location than the player’s currently in.
 
; is_christmas() : Classmethods that checks if the system clock matches Christmas.
 
; is_christmas() : Classmethods that checks if the system clock matches Christmas.
 
; is_halloween() : Classmethods that checks if the system clock matches Halloween.
 
; is_halloween() : Classmethods that checks if the system clock matches Halloween.
Line 442: Line 427:
 
; get_money(int money) : Add money to the player.
 
; get_money(int money) : Add money to the player.
 
; spend_money(int money) : Subtract money from the player.
 
; spend_money(int money) : Subtract money from the player.
; has_required_str(int min_str) : Check if the player has the required strength. Similar method exist for <code>int</code>, <code>chr</code> and <code>dex</code>.
+
; has_required_str(int min_str) : Check if the player has the required strength. Similar method exist for {{code|int}}, {{code|chr}} and {{code|dex}}.
 
; go_to(Location location) : Go to the given location.
 
; go_to(Location location) : Go to the given location.
 
; go_to_previous() : Go to the first parent location of the current location.
 
; go_to_previous() : Go to the first parent location of the current location.
; increase_str(int amount) : Increase the strength by amount. Defaults to 1. Similar method exist for <code>int</code>, <code>chr</code> and <code>dex</code>.
+
; increase_str(int amount) : Increase the strength by amount. Defaults to 1. Similar method exist for {{code|int}}, {{code|chr}} and {{code|dex}}.
  
 
== Miscellaneous functions and classes ==
 
== Miscellaneous functions and classes ==
Line 461: Line 446:
 
; format_seconds_to_dhm(int seconds) : Return a formatted string in the form (x)d (y)h (z)m from the number of seconds passed in the arguments.
 
; format_seconds_to_dhm(int seconds) : Return a formatted string in the form (x)d (y)h (z)m from the number of seconds passed in the arguments.
  
; insert_newlines(string string_to_format, int every) : Return a new string based on the passed in string, with newlines inserted every <code>every</code> character. <code>every</code> defaults to 30. Differs from <code>splice_string()</code> in that it is not cut a word in the middle if possible. <code>splice_string()</code> inserts a newline every <code>every</code> characters without question. <code>insert_newline()</code> is much safer as it will not cut a variable substitution, but is much slower than <code>splice_string()</code>.
+
; insert_newlines(string string_to_format, int every) : Return a new string based on the passed in string, with newlines inserted every {{code|every}} character. {{code|every}} defaults to 30. Differs from {{code|splice_string()}} in that it is not cut a word in the middle if possible. {{code|splice_string()}} inserts a newline every {{code|every}} characters without question. {{code|insert_newline()}} is much safer as it will not cut a variable substitution, but is much slower than {{code|splice_string()}}.
  
; text_identity(string text) : Return unmodified text, useful for <code>config.say_menu_text_filter</code> if it is <code>None</code>.
+
; text_identity(string text) : Return unmodified text, useful for {{code|config.say_menu_text_filter}} if it is {{code|None}}.
  
 
; replace_bracket(string text) : Return text without the [ and ] characters (Ren’Py variable formatting syntax).
 
; replace_bracket(string text) : Return text without the [ and ] characters (Ren’Py variable formatting syntax).
Line 469: Line 454:
 
; gauss(float mean, float deviation, float lower, float upper) : Return a random integer number in a normal distribution, clamped between lower and upper.
 
; gauss(float mean, float deviation, float lower, float upper) : Return a random integer number in a normal distribution, clamped between lower and upper.
  
; get_angle_speeds(int angle_width, Iterator angle_range, Iterator speed_range) : Return two lists – <code>true angles</code> and <code>false angles</code> – which are lists of tuples (<code>initial_angle, initial_speed</code>) for which the result land in or out of the <code>angle_width</code>. Used in [[Impregnation minigame]] and [[Spin the bottle minigame]].
+
; get_angle_speeds(int angle_width, Iterator angle_range, Iterator speed_range) : Return two lists – {{code|true angles}} and {{code|false angles}} – which are lists of tuples ({{nowrap|{{code|initial_angle, initial_speed}}}}) for which the result land in or out of the {{code|angle_width}}. Used in [[Impregnation minigame]] and [[Spin the bottle minigame]].
  
; safe_parse_dict(dict dct, object* keys, object default=None, list list_to_append=None) : Return the content of the dictionary at keys <code>keys</code>, subscribed in order. If a KeyError exception is raised, and a default is provided, then will return that default object. Any number of keys can be passed up to 253 (hard limit in CPython). Additionally, you can pass in a list to append the name of the missing keys of the dictionary to.
+
; safe_parse_dict(dict dct, object* keys, object default=None, list list_to_append=None) : Return the content of the dictionary at keys {{var|keys}}, subscribed in order. If a KeyError exception is raised, and a default is provided, then will return that default object. Any number of keys can be passed up to 253 (hard limit in CPython). Additionally, you can pass in a list to append the name of the missing keys of the dictionary to.
  
 
<u>'''Examples:'''</u>
 
<u>'''Examples:'''</u>

Latest revision as of 20:01, 13 July 2020

Warning red icon.pngThe API is depreciated: the definitions are incomplete and will not be updated for future versions. USAGE IS HIGHLY DISCOURAGED.

The Summertime Saga development team provides an application programming interface for those who would like to create or modify the game content (modding). In this article you will find documentation on the technical specifications and programming language. Find all the information concerning the modding on its own article.

FSMs (Finite State Machines)

Finite State Machines control the flow of the game for a particular character. They are basically linked chains of events (which are called States). The progress is achieved via a Trigger.

A basic example of a FSM

Creating a FSM

Creating a FSM is done in 3 steps:

  1. Create the states, triggers and a machine instance, providing the necessary arguments to each constructor. Triggers should be created in an init python block of priority 0 or later.
  2. Link the states together with State.add(Trigger t, State next_state) method.
  3. Add all the states to the machine with Machine.add(*State states) method.

State and machine definition and state linking should be done in a label named character_fsm_init, with character being the character’s name. Additionally, further edits to the machine (such as adding the states) should be done in a label named character_machine_init, with character being the name of the character.

Machine specific

The machine constructor has plenty of arguments, here is the detail and what is expected:

name 
A name for the machine used as a unique identifier for the machine. It’s also used to find the button dialogue for that machine.
default_loc 
A 4x7 matrix of locations to be used as the schedule for where a given machine should be at any point during the week. A 4x1 or 2x4 matrix may be passed as well, and internally will be converted to a 4x7 matrix. If a 1x4 matrix is passed, it’s assumed that the location only varies by time of day, and the day of the week doesn’t matter. A 2x4 matrix will split the two, the first 4-list is for weekdays, and the second for weekend days.
description, states 
(to be deprecated) An alternate ways to add states to the machine, and a description that is never used anywhere.
vars 
A dictionary containing variables (as strings, the keys of that dictionary), and init values for those variables. These variables are used with the state actions and the get(string variable) and set(string variable, object value) methods of the machine class. When a specific state doesn’t apply, or you need a variable to change based on the progress with a given path. The sex speed variable is assumed to be used only for setting the speed of the animation for sex scenes.

The machine class defines a lot of useful methods:

set_priority(int priority) 
Set the priority of the machine. Currently used to display the hints on the phone. A machine with a priority of 1 or more will show up in the phone. This may be used later to sort quests into main quests and side quests.
property button_dialogue 
Return the button dialogue for that machine (machine name + _button_dialogue), used in the TalkTo screen action.
image, show, say, and hide 
Now defunct methods.
property progress 
Return an integer that is the progress out of a 100%.
add_action(*actions) 
Add a machine action to this machine.
set_state(State state, bool null_delay=False) 
Revert the machine to init, then advance the machine until it is in the state state. If null_delay is True, set the delay of the state reached to 0.
machine_trigger 
Trigger for machine actions.
trigger(Trigger trigger, bool noactions=False) 
Trigger the machine to pass onto the next state. If noactions is True, don’t process the actions.
add(State* states) 
Add the states to the machine.
get(string var) 
Get the machine variable var.
set(string var, object value) 
Set the machine variable var.
get_state() 
Return the current state the machine is in.
property where 
Compute where the machine should be based on her default location, time of day, day of the week, forced location and if it’s forced or not for that time of day. Return a location.
is_state(State* states) 
Return boolean if the machine is in any of the provided states.
between_states(State stateBegin, State* statesEnd) 
Return boolean if the machine is between stateBegin and any of the statesEnd (non‐inclusive).
finished_state(State* states) 
Return boolean if the machine has finished any of the states provided.

State specific

You may want to add a delay to state creation by using the delay optional argument. That delay is the number of days until that state is effectively reached once the trigger has been “pulled”.

When linking states together, you may want to add state actions to be executed when the state is triggered. Those actions are:

['set','flag_1'] 
Set the value of flag_1 to True.
['clear','flag_1'] 
Set the value of flag_1 to False.
['toggle','flag_1'] 
Toggle the value of flag_1 between {{code|True and False.
['assign',['v1',100]] 
Set the value of v1 to 100.
['inc','v1'] 
Increase the value of v1 by 1.
['dec','v1'] 
Decrease the value of v1 by 1.
['triggeronzero':['v1',T_a_trigger]] 
Set {{{1}}}, and fire the trigger T_a_trigger if v1 is less than or equal to 0.
['trigger',T_a_trigger] 
Fire the trigger T_a_trigger.
['call','label'] 
Make a Ren’Py call to label. Label MUST return.
['location', [machine, {"tod":tod, "place":place}]] 
Set the forced location for the machine to place (moves the non‐player character).
tod is 1‐indexed (1 is morning, 2 is afternoon, 3 is evening, 4 is night).
['force', [machine, {"tod": list or int, "flag": 4-list or bool}]] 
Say if the location is forced at tod or sets force flags according to the 4-list provided.
['unforce', None/machine] 
Reset the locations for machine or the machine specified forced.
['exec', callable] 
Call the callable function or method.
['exec', [callable, *args]] 
Call the callable function or method and pass in the arguments args specified.
['condition', [condition_string, actions_list_true, actions_list_false, (optional) machine]] 
Execute the actions in actions_list_true if condition_string evaluates to True, otherwise executes actions_list_false. Conditions lists are assumed to be state actions.
['action', [target_machine, action, target]] 
Execute the state action on another machine.
['setdefaultloc', [[Location, Location, Location, Location]]] 
Set the default locations for the current machine.
The argument is in the same format as the default_loc argument in the machine constructor (1x4, 2x4 or 7x4 matrices).
['setoutfit', [location, outfit]] 
Set the outfit for that location. outfit may be either a string, or a 1x4, 2x4, 7x4 array of strings (similar to the locations).
['setnaked', True/False] 
Set is_naked attribute of the § Outfit manager to True or False for the current machine.
['setdefaultoutfit', [outfit, {'tod':tod, 'dow':dow}]] 
Set the default outfit for the current machine.
tod and dow can be omitted. outfit is a required argument in the format of a string or a 1x4, 2x4, 7x4 matrix.
If tod and dow are omitted, outfit cannot be a matrix, but only a string. A workaround solution exists by passing the {"tod":None,"dow":None} dictionary.

Editing an FSM

Editing an existing Finite State Machine is as simple as calling the clear() method of the state you want to clear. It takes one argument, cleardelay, which defaults to False. If cleardelay is True, then the delay of the state is cleared. If you don’t want to edit the state but clear the delay, just set the State.delay attribute to 0 instead.

Once cleared, the state is unlinked to the FSM, which means that story will block at that state. It’s up to you to link that state to the rest of the machine, with additional states in the middle for your code.

Using FSMs

Using FSMs is easy. In the location labels, the machine state should be tested with is_state(State* states) method. Multiple states may be passed in that method, as well as a list of states. If the machine is in any of the provided states, then the method returns True, and False otherwise. For convenience, similar methods are used: between_states(State state1, State state2) returns True if the machine is in any state between state1 and state2 but is not in state state2; finished_state(State* states) returns True if the machine has finished any of the provided states.

To advance to the next state, trigger(Trigger t) method of the machine class is used. It moves the machine to the next state associated with that trigger if the provided trigger is in the current state table, and doesn’t do anything otherwise.

Example of FSM

init python:
    T_cassie_ban_mc = Trigger("ban mc")
    T_cassie_lift_ban = Trigger("lift ban")
    T_cassie_drowning = Trigger("drowning")
    T_cassie_end = Trigger("end")

label cassie_fsm_init:
    python:
        # Cassie’s States
        S_cassie_start = State("start")
        S_cassie_ban_from_pool = State("ban from pool", "You should go to the pool at night...")
        S_cassie_caught_skinny_dipping = State("caught skinny dipping", "Cassie lifted your ban! Go for a swim!")
        S_cassie_medic_room = State("medic room", "Have fun in the medic room...")
        S_cassie_end = State("end")
        
        # Build out Cassie’s FSM
        S_cassie_start.add(T_cassie_ban_mc, S_cassie_ban_from_pool)
        S_cassie_ban_from_pool.add(T_cassie_lift_ban, S_cassie_caught_skinny_dipping)
        S_cassie_caught_skinny_dipping.add(T_cassie_drowning, S_cassie_medic_room)
        S_cassie_medic_room.add(T_cassie_end, S_cassie_end, actions=["exec", A_drowning_in_pussy.unlock])
        
        M_cassie.add(S_cassie_start, S_cassie_ban_from_pool,
                     S_cassie_caught_skinny_dipping,
                     S_cassie_medic_room, S_cassie_end)
    return

label cassie_machine_init:
    python:
        M_cassie = Machine("cassie", default_loc=[[L_pool, L_pool, L_pool, L_NULL]],
                         vars={'sex speed': .3,
                               'had sex': False},
        )
    return

At init time, all the triggers are created. It’s important that triggers be created at an init time of 0 or later, since the Trigger class is defined in an init -2 python block, and other parts of FSMs in an init -1 python block.

On starting the game, and on loading a save file or reloading the game, the states are created and linked together, but after the machine itself is initialized. Here is the order applies to the previous example:

init (triggers) > call (after_load/start) > call cassie_machine_init > call cassie_fsm_init

Explication: the machine is named M_cassie and is given the name cassie. This name is used in several places throughout the code to refer to that object. Furthermore, Cassie’s default location (default_loc argument) specifies that the character is at the pool from morning to evening, and nowhere at night. A dictionary is initialized as well to keep track of some other variables, like the sex animations speed, the previous sexual interactions with that character, the variables to trigger repeatable dialogues, counters, etc.
In the same label, you can set up the outfit system of that character, as seen in the next section, or add machine actions to the FSM. This is also the place to set the priority of the Finite State Machine.

The states are created and linked in the label cassie_fsm_init. In the given example, the last link has a state action attached to it, which means that on reaching the S_cassie_end state, the machine automatically calls the unlock method for the A_drowning_in_pussy achievement. In the same way, other state actions may be defined.

More examples:

S_diane_drunken_splur.add(T_diane_help_carry_to_bed, S_diane_get_cold_towel,
                                  actions = ["location", {"place": L_diane_bedroom},
                                             "force", {"tod": [0,1]},
                                             ]
                                  )
        S_diane_milking_help.add(T_diane_milking_malfunction_help, S_diane_debbie_evening_visit,
                                 actions = ["location", {"place": L_home_kitchen,
                                                         "condition": "not M_diane.is_set('first cucumber')",
                                                         },
                                            "force", {"tod": 2},
                                            ]
                                 )
S_diane_look_in_kitchen.add(T_diane_search_kitchen, S_diane_seen_cucumber,
                                    actions = ["action", [M_player, "set", "jerk diane"]
                                               ]
                                    )
S_diane_fetch_pump.add(T_diane_found_pump, S_diane_delivery_2_task,
                               actions = ["setdefaultloc", [[L_diane_shed, L_diane_shed, L_diane_shed, L_diane_bedroom]]]
                               )

Outfit manager

Every FSM has an outfit manager instance attached to it. This system is designed a bit like the Location system: each outfit has a specific time of day/day of week schedule for which the character is wearing that outfit. The manager also considers the current location and handles whether the character is naked or not, with the is_naked attribute.

To bind the schedule of an outfit to a specific location, use the method bind_outfit_to_location, with the location as first argument, and the outfit string or schedule as second argument.

An outfit schedule is very similar to a location schedule, it’s a 1x4/2x4/7x4 matrix of strings, each of those strings being the outfit of a given pair of time of day/day of week.

Init arguments:

default_outfit 
Defaults to '"`UNIQ--nowiki-00000041-QINU`"'.
is_naked 
Defaults to False.

Methods and attributes:

default_outfit 
The outfit schedule used if the character is in a location that doesn’t have a table of outfits for that character.
is_naked 
Boolean that tells if the machine is naked or not.
bind_outfit_to_location(Location location, object outfit_schedule) 
Bind the given outfit schedule to the given location. The outfit schedule may be a string, a 1x4 matrix, a 2x4 matrix or a 7x4 matrix of strings.
format_outfit_schedule(object outfit_schedule) 
Format the outfit schedule to a 7x4 matrix of strings. Used internally.
property get() 
Return the proper outfit given the current time of day, day of week, whether the machine is naked or not, and current location.
Important icon orange.pngBelow is a copy‐paste from Discord. It gets the point across though.

So basically how the new outfit system works; you have to setup the outfit manager. The same manager has one method called bind_outfit_to_location that takes 2 arguments: location, a location object, and outfit, which can be an outfit string or an outfit schedule, i.e. a matrix just like the locations. This method creates a map of outfits to use in certain conditions.

Moreover, the outfits can be set with a simple state action which takes care of calling the bind_outfit_to_location method. Now, to get the outfit, it’s as simple as calling M_diane.outfit.get (without any parentheses as get is a property of the outfit manager). The variable is_naked has also been moved to the outfit manager.

Instead of M_diane.get_naked_str, use M_diane.outfit.get; and instead of doing {{{1}}}, configure the outfit manager with M_diane.outfit.bind_outfit_to_location(L_home_livingroom, "casual"). If you want more granularity, write M_diane.outfit.bind_outfit_to_location(L_home_livingroom, '"`UNIQ--nowiki-0000004C-QINU`"'). You can even set that matrix to bean individual outfit for each time of day, each day of the week.

setnaked and setoutfit are dedicated to the state action:

  • setnaked sets the machine to be naked or not, thing you can also do with {{{1}}} (True or False).
  • setoutfit calls bind_outfit_to_location method with two arguments specified like that: {{{1}}}

Example for Machine.outfit.get property:

if the machine is naked
    return "naked"
otherwise if the player is in a location the outfit manager is not bound to
    if the outfit to be returned is "naked", set the is_naked attribute to True, and False otherwise
    return default_outfit for that day of week and time of day
otherwise
    if the outfit to be returned is "naked", set the is_naked attribute to True, and False otherwise
    return outfit for that location at that day of week and time of day

Use del M_diane.outfit.outfits[L_home_livingroom] to revert back to the default if needed.

Pregnancy manager

Dating System

The dating system was introduced, as a way to limit the impact it would have on future save and compatibility. It functions through dating_points. Dating points are a secondary currency that is specific to each machine, and that you earn by performing tasks with said machines.

The dating.json file

Each datable state machine has an entry in that file, which specifies what are this machine’s likes and dislikes, among other things. Example:

"roxxy": {
        "activities": {
            "movies": 5,
            "eating": 3,
            "swimming": 1
        },
        "gifts": {
            "crystal_necklace": 5,
            "pearl_necklace": 4,
            "heart_necklace": 3,
            "plush_*": 2,
            "flower_*": 1
        },
        "dialogues": {
        },
        "thresholds": {
            "sex": 30
        }
    }
  • activities : possible activities with this datable machine, include things like picnics, eating at a restaurant, swimming, going to the movies
  • gifts : possible gifts to give to this machine. Items from Cupid are giftable, but other items may be gifted as well, depending on the machine. June might like videogames found at Cosmic Cumics for instance
  • dialogues : possible dialogues tied to dating, that are triggered when talking to the character. Some dialogues may be tied to a threshold.
  • thresholds : dating_points thresholds the machine must reach beefore certain events may occur, like dialogues, sex, oral sex, foreplay and so on.

Each like or dislike is rated on a scale of -5 to 5, corresponding to the amount of dating points one earns when performing that action.

Dating points exponential decrease

The more one performs a specific action with a machine, the less points are awarded for performing said action. The formula is as follows:

{{{1}}}, t = number of times the action was performed.

The result is then rounded, and cast as an integer before being added to the total dating points.

Locations

Locations handle all the locations in the game. They are represented as a tree, with the possibility of multiple parents (like Mrs. Smith’s bedroom, which has the front yard and the hallway as its parents). A required attribute is the location name.

Creating a location

Locations are easy to create. It’s as simple as instantiating the Location class, and providing a name for that location to the constructor (which is the only required argument). Locations are mutable, so they should be instantiated in a modname_locations_init label.

Furthermore, a locations has several optional arguments:

name 
The name for that location, as can be seen in the top‐left corner of the screen. It’s also the way to find and call the relevant location screen, as well as the location label.
unlock_popup 
The name of a Ren’Py‐defined displayable for the popup that should show up when the location is unlocked.
background 
The name of the background used for that location. The background name must follow several conventions to properly work:
  • Must be in the folder images/backgrounds/
  • Name must start with location_
  • Name must end with _day.jpg, _evening.jpg or _night.jpg, respectively for day, evening or night backgrounds.
  • For Halloween or Christmas backgrounds, the strings _halloween and _christmas must be added before the time of day code. This will allow the game to find relevant backgrounds for the time period.
Whatever is leftover should be provided in the background argument of the constructor. The full file path will be constructed by the background (respectively background_blur) properties.
parents 
Either a list of location or a single location instance. Provide the parents of that location. If left empty, the location is assumed to be the root of the tree. The root should always be L_map (or L_NULL, but L_NULL should not have children).
locked 
Defaults to False. Whether that location should be initially locked, and inaccessible at the beginning of the game.
label 
(to be deprecated) The label name for that location. formatted_name property will be used later on.

Example:

L_diane_barn = Location("Diane's Barn", unlock_popup="popup_diane_barn", background="barn_frontyard", parents=L_map, locked=True)

This code defines a location named "Diane's Barn" (formatted name: dianes_barn), child of L_map, that shows popup_diane_barn on unlock, and that is initially locked. The background is set as barn_frontyard, which means the game looks for files named backgrounds/location_barn_frontyard_day.jpg or backgrounds/location_barn_frontyard_night.jpg for instance.

User‐defined screen actions

The screen actions are specific to Summertime Saga and should be used to keep the flow consistent.

MoveTo

This action expects a location to be passed. It hides the current screen, moves the player to that location, and calls the corresponding screen as well as jumps to the location label.

BuyItem

This action expects an item, or item identifier string to be passed to it. When activated, buys the item, and shows the appropriate failing popups should the transaction fail. Optionally, you may add a callback label with the callback_label optional argument. If the transaction succeeds, the game then calls that label. Useful if you want some story element to trigger after buying an item.

TalkTo

This action is used to talk to the character. It hides the screen and start the conversation with the given character. It expects a machine instance, or a string to identify it, in which case it attempts to find the machine in the store.machines dictionary.

ClearPersistent

This action resets all persistent data (cookie jar, time spent playing, achievements, etc.). No arguments expected.

GetItem

This action gets the item, and attempts to show the corresponding popup. No arguments required.

JSON Data

Items and inventory management

The inventory manager is fully automatic, but you may want to add some items to the game. Items are stored in items.json file located in scripts/data/folder.

Example:

"birth_control_pills": {
    "id": "173",
    "name": "{b}Birth Control Pills:{/b}",
    "cost": "0",
    "image": "objects/item_pills3.png",
    "description": "Makes you temporarily sterile.",
    "closeup": "",
    "dialogue_label": "birth_control_pills",
    "popup_image": ""
}

In order:

  • birth_control_pills: the item identifier.
  • id: a numeric id. Unused at this moment.
  • name: the item name, as seen in the inventory.
  • cost: the item cost. It’s cast as an integer, and prevents the player from picking up the item if he doesn’t have sufficient money.
  • image: the item miniature image, as seen in the inventory.
  • description: the item description, as seen in the inventory.
  • closeup: the item close‐up image, if there is one.
  • dialogue_label: the item dialogue label. It’s triggered if it exists, on clicking the item. The label should always return, and take an item argument.
  • popup_image: the item popup image. It’s displayed when first acquiring the item, leave the string empty if there is none.

Text messages

Text messages are stored in this format in text_messages.json file. Once defined there, you can use player.receive_message(message_id) for the player to receive the message.

Example:

"mia02": {
    "sender": "mia",
    "content_preview": "We can't find {b}my dad{/b}...",
    "content": "We can't find {b}my dad{/b}...\nCan you come help us please?",
    "image": "cellphone/cellphone_text_mia01.png"
}

In order:

  • mia02: the message identifier. Must be unique. Message identifiers ending in _pregnancy or _pregnancy_labor are reserved for the pregnancy system messages.
  • sender: the name of the machine that sent this message.
  • content_preview: the preview text displayed on the phone main screen.
  • content: the actual message displayed when clicking the message in the phone.
  • image: the image used as a profile picture on the phone.

Achievements

Achievements are defined in achievements.json file.

Example:

"angler": {
    "id": 1,
    "name": "The Angler",
    "description": "Catch one of every type of fish.",
    "hidden": false,
    "enabled": true,
    "image": "achievements/cellphone_achieve_01.png"
}

In order:

  • angler: the achievement identifier. Must be unique. The identifier is used to name the achievement variable. Dashes are replaced with underscores. All achievements are prefixed with “A_”.
  • id: a numeric id. Unused.
  • name: the achievement’s name.
  • description: the achievement’s description. New lines are inserted every 30 characters, words are not split in the middle.
  • hidden: whether it’s a secret achievement or not.
  • enabled: if the achievement can be achieved.
  • image: the icon for this achievement.

Dialogues

The dialogues.json file is used for the dialogue lines written by the higher tier contributors of DarkCookie’s Patreon.

Game manager class

The game manager class handles everything that is related to the gameplay itself, but is not planned to be extendable by mods. Its methods and attributes are still usable and should be used for your mod.

language 
Set the language of the game which is used for translations. Defaults to "en" for English with other languages in the game untranslated.
cheat_mode 
Return True if the game is currently in cheat mode (thus allowing minigames to be skipped).
CA_FILE 
Path to a certification file used to enable requests to websites. Used only if the player has checked the option “Allow Internet Access”.
lock_ui() 
Lock the user interface.
unlock_ui() 
Unlock the user interface.
ui_locked() 
Return whether the user interface is locked or not (i.e. grayed‐out).
dialog_select(string label_name) 
Classmethod that chooses a label based on its name and the language class attribute. To be used to split dialogues and logic, and also allows your mod to be easily translated.
choose_label(string template) 
Classmethod that chooses a label at random that matches the template passed in the arguments.
main() 
Method that is to be called at the end of every label the player jumps to (and not called to). It calls the player’s location screen, and checks for achievements and other stuff. This would be the only method expandable upon. It also clears the return stack, so that traceback are not indigestible. It may take two arguments, clear_return_stack to enable or not return stack clearing, and location, to call the screen of another location than the player’s currently in.
is_christmas() 
Classmethods that checks if the system clock matches Christmas.
is_halloween() 
Classmethods that checks if the system clock matches Halloween.

Player class

The player class handles everything related to the player: the inventory, grades, vehicle level and stats. Notable methods:

receive_message(string message_id) 
Check if the player has received the given text message on the phone. Also sets up the alert icon on the user interface.
has_item(string* items) 
Check if the player has any of the items provided at this moment.
has_picked_up_item(string* items) 
Check if the player has ever picked up any of the items provided.
get_item(string item) 
Get the item if the price of the item doesn’t exceed the amount of the player’s money.
remove_item(string item) 
Remove an item by its string id.
get_money(int money) 
Add money to the player.
spend_money(int money) 
Subtract money from the player.
has_required_str(int min_str) 
Check if the player has the required strength. Similar method exist for int, chr and dex.
go_to(Location location) 
Go to the given location.
go_to_previous() 
Go to the first parent location of the current location.
increase_str(int amount) 
Increase the strength by amount. Defaults to 1. Similar method exist for int, chr and dex.

Miscellaneous functions and classes

KeepRefs 
A class used everywhere to make sure to keep a reference to any object instantiated, making sure is checks are kept correct.
get_instances() 
Classmethod to get a generator of all the instances of this class subclass.
LastUpdatedOrderedDict 
A dictionary‐like structure that keeps its order, and store its items in the order the keys were last added (and not updated). This is a subclass of Python’s OrderedDict from the collections package.
listvalues 
Return a list of all the values of this dictionary.
listkeys 
Return a list of all the keys of this dictionary.
lastkey 
Return the last key added to this dictionary.
lastvalue 
Return the value of the last key added to this dictionary.
isempty 
Return whether the dictionary is empty.
format_seconds_to_dhm(int seconds) 
Return a formatted string in the form (x)d (y)h (z)m from the number of seconds passed in the arguments.
insert_newlines(string string_to_format, int every) 
Return a new string based on the passed in string, with newlines inserted every every character. every defaults to 30. Differs from splice_string() in that it is not cut a word in the middle if possible. splice_string() inserts a newline every every characters without question. insert_newline() is much safer as it will not cut a variable substitution, but is much slower than splice_string().
text_identity(string text) 
Return unmodified text, useful for config.say_menu_text_filter if it is None.
replace_bracket(string text) 
Return text without the [ and ] characters (Ren’Py variable formatting syntax).
gauss(float mean, float deviation, float lower, float upper) 
Return a random integer number in a normal distribution, clamped between lower and upper.
get_angle_speeds(int angle_width, Iterator angle_range, Iterator speed_range) 
Return two lists – true angles and false angles – which are lists of tuples (initial_angle, initial_speed) for which the result land in or out of the angle_width. Used in Impregnation minigame and Spin the bottle minigame.
safe_parse_dict(dict dct, object* keys, object default=None, list list_to_append=None) 
Return the content of the dictionary at keys keys, subscribed in order. If a KeyError exception is raised, and a default is provided, then will return that default object. Any number of keys can be passed up to 253 (hard limit in CPython). Additionally, you can pass in a list to append the name of the missing keys of the dictionary to.

Examples:

safe_parse_dict({"a":{"b":2}}, "a", "c", default="default return value")
# returns "default return value"
safe_parse_dict({"a":{"b":2}}, "a", "b", default="default return value")
# returns 2
safe_parse_dict({"a":{"b":2}}, "a", "b", "default return value")
# prints "Extra positional argument passed, is default properly named in the arguments?" and returns 2
safe_parse_dict({"a":{"b":2}}, "a", "c", list_to_append=missing_keys)
# appends "c" to list missing keys and returns None. If a default argument is passed, will return default