Skip to content

Commit

Permalink
Version 0.1.0. Launch of public beta.
Browse files Browse the repository at this point in the history
* Fixed a display bug when refreshing the list of properties associated with a character on the character editing screen.
* Selecting a property on the property creation screen will now load the property for editing; double-clicking is no longer needed. However, only one property can be selected at a time, now.
* Fixed a bug involving authored properties that applied to specific characters rather than the entire cast.
* Changed lapis lazuli theme to use custom close icons for popup windows.
* Changed titles of PersonalityModel and GraphView tabs to have spaces between the words.
* Updated README.txt file.
* Changed patron lists in editor.
* Added tooltips to add, delete, move, and edit buttons on various screens in editor.
* Made minor changes to how property creation screen looks, to bring it more in line with how other screens look.
* Fixed a bug on the settings screen so that it will now correctly display the save path.
* Updated download link on updates screen.
* Updated project load functions and incremented version number from 0.0.38 to 0.1.0.
  • Loading branch information
FrobozzWaxwing committed Apr 8, 2023
1 parent 999df4f commit c084fd3
Show file tree
Hide file tree
Showing 26 changed files with 305 additions and 221 deletions.
139 changes: 13 additions & 126 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,140 +1,27 @@
# SweepWeave

SweepWeave is an integrated development environment for creating interactive storyworlds. Storyworlds are computer games, or interactive fiction works, which focus on interaction with fictional characters as the central game mechanic, which track dynamic relationships between characters, (including, in particular, between non-player characters and the player character,) and which employ these simulated characters and relationships to assemble pieces of handcrafted content into a coherent narrative shaped, in part, by player choice.
SweepWeave is an integrated development environment for creating interactive storyworlds. Storyworlds are computer games which focus on interaction with fictional characters as the central game mechanic, which track dynamic relationships between characters, and which employ these simulated characters and relationships to assemble pieces of handcrafted content into a coherent narrative shaped, in part, by player choice.

SweepWeave is based on design work by Chris Crawford and Sasha Fenn, and has been coded and implemented by Sasha Fenn. The suite consists of a storyworld interpretter, written in html and javascript, and a storyworld editor, written in the Godot game engine. Storyworlds are written and scripted in the editor, projects can be saved as .json files, and storyworlds can be exported as standalone html pages, which combine the interpreter with a block of data unique to the storyworld, and which can be played in a web browser.
SweepWeave is based on design work by Chris Crawford and Sasha Fenn, and has been coded and implemented by Sasha Fenn. The suite consists of a storyworld interpreter, written in html and javascript, and a storyworld editor, written in the Godot game engine. Storyworlds can be exported as standalone html pages, which can be played in a web browser.

Storyworlds consist of a collection of characters and a collection of "encounters," which contain the game's story content.

***********
Characters:
***********

Each character has personality traits, which track their tendency to act in certain ways, and traits that track their relationship to the player character over the course of the game.

Both personality traits and relationship traits are tracked via "bounded numbers," which are numbers limited to the range: -1 to 1, (including the lower and upper limits within the range.)

***********
Encounters:
***********

Each encounter includes a title, a piece of main text that can describe events that happen within the story, a set of options for the player to choose from, such that they can respond to story events, and a set of reactions associated with each of these options, such that a specified character can react to the player's choice.

Authors can also change the settings of the encounter and the options available to the player, changing when encounters occur or when certain options are available for players to choose.

Each encounter has a single character associated with it as an "antagonist;" this character's personality and relationship traits are used by the game to determine which reaction occurs after the player has chosen an option. Each reaction has an associated script by which the game calculates the character's inclination to select that reaction; during play, the reaction with the highest inclination is selected. (Ties are broken by choosing the earlier reaction in the list of reactions for the option in question.)

Once a reaction has been selected by the a.i., the game may also change the relationships between any non-player character and the player character. This allows authors to set certain consequences to occur as a result of the player's choices. In turn, these consequences can carry over to later parts of the story, since character relationships can affect which encounters occur, at what time they occur, and how characters react to the player's actions.

Reactions may also cause a specific encounter to occur next, bypassing the usual methods used by the game to select which encounter occurs.

*********
Gameplay:
*********

A playthrough runs through the following process:

1) The game selects an encounter to display.
1-A) If the reaction selected during the last encounter specifies an encounter to occur next, this encounter is displayed.
1-B) Otherwise, the game tests each encounter in the storyworld, checking when it can occur and whether all of its prerequisites are met.
1-C) The game then calculates the desirability of each valid encounter, displaying the most desirable encounter and displaying it to the player.
2) The game displays an encounter.
2-A) The encounter's main text is displayed to the player.
2-B) The game calculates each option's visibility and performability prerequisites, displaying only those which meet their visibility prerequisites, and only allowing players to choose one that meets both its visibility and performability prerequisites.
2-C) If no options pass their visibility and performability prerequisites, or the encounter has no options, then the game ends with this encounter.
3) The player chooses an option.
4) The game selects a reaction for the encounter's antagonist to perform, and displays the text of this reaction to the player.
4-A) The game calculates the inclination of each reaction associated with the option chosen by the player. This is done using the scripts associated with each reaction, which use the antagonist's personality and relationship traits.
4-B) The reaction with the highest inclination is selected.
4-C) The game resets the options panel to display only the option the player chose.
4-D) The text of the selected reaction is displayed to the player. This text can, for example, describe the antagonist's actions in response to the player's choice.
4-E) The relationship changes associated with the selected reaction are calculated, and the game changes the designated character relationships accordingly. (The relationships of any character in the game can be changed, during this phase.)
4-F) The game takes note of whether or not the selected reaction forces a specific encounter to occur next, and, if so, of which encounter must occur.
5) The game returns to step 1, repeating this loop until the game runs out of valid encounters, or reaches an encounter without any performable options, at which point the story ends.

**********
Scripting:
**********

Storyworlds employ scripts for several sets of calculations. This section contains some technical details for those interested.

1) Reactions:

When the game calculates the inclinations for each reaction associated with a given option, it looks up two of the current antagonist's traits and blends them together. This "blend" operation is a weighted average of the two traits. A weighting factor of -1 will set the result equal to the first trait, a weight of 1 will set it equal to the second trait, and a weight of 0 will set it equal to the simple average of both traits, (i.e., adding up the value of both traits and dividing by two.)

If one normalizes the weight from a number between -1 and 1 to a number between 0 and 1, (which can be done by averaging the weight with the number 1,) then the algorithm for a blend calculation consists of the following:

Result = x * (1 - w) + y * w.

Here, "x" equals the value of the first trait, y equals the value of the second trait, and w equals the value of the normalized weight.

2) Consequences:

Once a reaction occurs, the game may change the value of various character relationships. Authors can determine what modifications are made by setting up "blend change" scripts. Simply select the character and pValue to modify, then choose a weight.

A weight of 0 will leave the relationship unchanged. A weight above 0 will increase the value of the relationship trait, (i.e., the selected pValue.) A weight below 0 will decrease the pValue.

The change made either increases or decreases the pValue a certain percentage of the distance towards the upper or lower limit. For example, a weight of 1 will increase the pValue 100% of the distance towards 1, (i.e., the pValue will be set to 1.) A weight of 0.5 will increase the pValue 50% of the distance from its present value to 1. For example, if the character's current level of affection for the player character equals 0.2, and the designated weight equals 0.5, then the character's affection for the player character will increase to 0.6.

The algorithm for blend change operations consists of the following:

Result = x * (1 - absolute_value_of(delta)) + delta.

Here, "x" is the current value of the relationship trait in question, and "delta" is the weighting factor.

3) Encounter Settings:

Encounters can have both prerequisites and target conditions. Prerequisites allow authors to set encounters to only occur when certain conditions are met, for example when another encounter has occurred earlier in the playthrough. Target conditions are used by the game to calculate the desirability of each encounter.

Target conditions use a distance calculation to determine the encounter's desirability.

4) Option Settings:

Options can also have prerequisites, which determine whether or not an option is visible should the encounter occur, as well as whether the player can choose the option when it is visible.

********
Credits:
********
To run SweepWeave on Linux, you will need to manually change the permissions of the executable file, (the .x86_64 file,) after downloading it so that you can run the program.

SweepWeave is available as open source software under an MIT License. All storyworlds created with SweepWeave belong to their respective authors. You are free to publish your storyworlds as you please, including for commercial purposes.

Updates regarding SweepWeave's development can be found at https://sweepweave.org/
SweepWeave can be downloaded from https://github.com/FrobozzWaxwing/Sweep_Weave/releases
Discussion of storyworld design and development can be had on Discord: https://discord.gg/a3aXjtAuDF
Chris Crawford can be found online at http://erasmatazz.com/
Sasha Fenn can be found online at https://www.patreon.com/sasha_fenn
SweepWeave can be downloaded from http://www.emptiestvoid.com/encounter_system/

If you would like to help support me financially, you can do so through my Patreon page, reached via the second link above.
If you would like to help support me financially, you can do so through my Patreon page:
https://www.patreon.com/sasha_fenn

Thanks to my patrons for their encouragement and support:
Pamela Beck
Chris Conley
Jóhannes Ævarsson

You can contact me through Chris Crawford's Thaddeus discussion board, through our interactive storytelling workgroup on Slack, via github, or through my Patreon page. On github I am FrobozzWaxwing.
Felipe Vega
Pixel Brownie Software
Craig Maloney

Thanks, and peace to you.
~Sasha Fenn

********
License:
********

MIT License

Copyright (c) 2020-2021 ~ Sasha Fenn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
~Sasha Fenn
18 changes: 11 additions & 7 deletions godot/Actor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func set_as_copy_of(in_character, create_mutual_links = true):
bnumber_properties = in_character.bnumber_properties.duplicate(true)
authored_properties = []
authored_property_directory = {}
#Sanity check:
for property in in_character.authored_properties:
#Sanity check:
if (is_instance_valid(property)):
index_authored_property(property)
if (create_mutual_links):
Expand All @@ -56,14 +56,18 @@ func set_as_copy_of(in_character, create_mutual_links = true):

func remap(to_storyworld):
storyworld = to_storyworld
var properties_to_index = authored_properties.duplicate()
authored_properties = []
authored_property_directory = {}
for property_blueprint in storyworld.authored_properties:
if (property_blueprint.attribution_target == property_blueprint.possible_attribution_targets.ENTIRE_CAST):
index_authored_property(property_blueprint)
elif (property_blueprint.attribution_target == property_blueprint.possible_attribution_targets.CAST_MEMBERS):
if (property_blueprint.affected_characters.has(self)):
index_authored_property(property_blueprint)
for property_blueprint in properties_to_index:
if (storyworld.authored_property_directory.has(property_blueprint.id)):
index_authored_property(storyworld.authored_property_directory[property_blueprint.id])
# for property_blueprint in storyworld.authored_properties:
# if (property_blueprint.attribution_target == property_blueprint.possible_attribution_targets.ENTIRE_CAST):
# index_authored_property(property_blueprint)
# elif (property_blueprint.attribution_target == property_blueprint.possible_attribution_targets.CAST_MEMBERS):
# if (property_blueprint.affected_characters.has(self)):
# index_authored_property(property_blueprint)

func set_classical_personality_model(in_Bad_Good, in_False_Honest, in_Timid_Dominant, in_pBad_Good, in_pFalse_Honest, in_pTimid_Dominant):
initialize_bnumber_properties()
Expand Down
34 changes: 23 additions & 11 deletions godot/AuthoredPropertyCreationScreen.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ func load_authored_property(property_blueprint):
$PropertyCreationWindow/VBC/BNumberEditPanel.creating_new_property = false
$ColorRect/VBC/HBC/BNumberEditPanel.refresh()
if (!storyworld.authored_properties.empty()):
$ColorRect/VBC/HBC/VBC1/PropertyList.select(storyworld.authored_properties.find(property_blueprint))
$ColorRect/VBC/HBC/VBC/PropertyList.select(storyworld.authored_properties.find(property_blueprint))

func refresh_property_list():
$ColorRect/VBC/HBC/VBC1/PropertyList.clear()
$ColorRect/VBC/HBC/VBC/PropertyList.clear()
var item_index = 0
for property_blueprint in storyworld.authored_properties:
$ColorRect/VBC/HBC/VBC1/PropertyList.add_item(property_blueprint.get_property_name())
$ColorRect/VBC/HBC/VBC1/PropertyList.set_item_metadata(item_index, property_blueprint)
$ColorRect/VBC/HBC/VBC/PropertyList.add_item(property_blueprint.get_property_name())
$ColorRect/VBC/HBC/VBC/PropertyList.set_item_metadata(item_index, property_blueprint)
item_index += 1
if (!storyworld.authored_properties.empty()):
load_authored_property(storyworld.authored_properties.front())

func refresh_property_name(property_blueprint):
var index = storyworld.authored_properties.find(property_blueprint)
if (property_blueprint == $ColorRect/VBC/HBC/VBC1/PropertyList.get_item_metadata(index)):
$ColorRect/VBC/HBC/VBC1/PropertyList.set_item_text(index, property_blueprint.get_property_name())
if (property_blueprint == $ColorRect/VBC/HBC/VBC/PropertyList.get_item_metadata(index)):
$ColorRect/VBC/HBC/VBC/PropertyList.set_item_text(index, property_blueprint.get_property_name())
else:
refresh_property_list()
emit_signal("refresh_authored_property_lists")
Expand Down Expand Up @@ -75,7 +75,7 @@ func _on_DeleteButton_pressed():
each.call_deferred('free')
#Refill window.
var new_label = Label.new()
var selected_indices = $ColorRect/VBC/HBC/VBC1/PropertyList.get_selected_items()
var selected_indices = $ColorRect/VBC/HBC/VBC/PropertyList.get_selected_items()
authored_properties_to_delete.clear()
if (0 == selected_indices.size()):
#No properties are currently selected.
Expand All @@ -86,15 +86,15 @@ func _on_DeleteButton_pressed():
new_label.text = "You cannot delete all authored properties from your storyworld."
$ConfirmPropertyDeletionWindow/VBC.add_child(new_label)
elif (1 == selected_indices.size()):
var property_blueprint = $ColorRect/VBC/HBC/VBC1/PropertyList.get_item_metadata(selected_indices[0])
var property_blueprint = $ColorRect/VBC/HBC/VBC/PropertyList.get_item_metadata(selected_indices[0])
new_label.text = "Are you sure that you want to remove the \"" + property_blueprint.get_property_name() + "\" property from your storyworld?"
$ConfirmPropertyDeletionWindow/VBC.add_child(new_label)
authored_properties_to_delete.append(property_blueprint)
else:
new_label.text = "Are you sure that you want to remove the following authored properties? This will delete these properties from all characters and scripts."
$ConfirmPropertyDeletionWindow/VBC.add_child(new_label)
for index in selected_indices:
var property_blueprint = $ColorRect/VBC/HBC/VBC1/PropertyList.get_item_metadata(index)
var property_blueprint = $ColorRect/VBC/HBC/VBC/PropertyList.get_item_metadata(index)
authored_properties_to_delete.append(property_blueprint)
new_label = Label.new()
new_label.text = property_blueprint.get_property_name()
Expand Down Expand Up @@ -164,17 +164,29 @@ func _on_ConfirmPropertyDeletionWindow_confirmed():
refresh_property_list()
emit_signal("refresh_authored_property_lists")

func _on_PropertyList_item_activated(index):
var property_blueprint = $ColorRect/VBC/HBC/VBC1/PropertyList.get_item_metadata(index)
func _on_PropertyList_item_selected(index):
var property_blueprint = $ColorRect/VBC/HBC/VBC/PropertyList.get_item_metadata(index)
load_authored_property(property_blueprint)

func on_character_properties_changed(property, character):
emit_signal("refresh_authored_property_lists")

#GUI Themes:

onready var add_icon_light = preload("res://custom_resources/add_icon.svg")
onready var add_icon_dark = preload("res://custom_resources/add_icon_dark.svg")
onready var delete_icon_light = preload("res://custom_resources/delete_icon.svg")
onready var delete_icon_dark = preload("res://custom_resources/delete_icon_dark.svg")

func set_gui_theme(theme_name, background_color):
$ColorRect.color = background_color
$ColorRect/VBC/ColorRect.color = background_color
$ColorRect/VBC/HBC/BNumberEditPanel.set_gui_theme(theme_name, background_color)
$PropertyCreationWindow/VBC/BNumberEditPanel.set_gui_theme(theme_name, background_color)
match theme_name:
"Clarity":
$ColorRect/VBC/HBC/VBC/HBC/AddButton.icon = add_icon_dark
$ColorRect/VBC/HBC/VBC/HBC/DeleteButton.icon = delete_icon_dark
"Lapis Lazuli":
$ColorRect/VBC/HBC/VBC/HBC/AddButton.icon = add_icon_light
$ColorRect/VBC/HBC/VBC/HBC/DeleteButton.icon = delete_icon_light
Loading

0 comments on commit c084fd3

Please sign in to comment.