├── addons └── inkgd │ ├── mono │ ├── assemblies │ │ └── .gitkeep │ └── InkBridger.cs │ ├── editor │ ├── templates │ │ ├── .gdignore │ │ ├── ink_template.gd │ │ └── ink_template_signals.gd │ ├── panel │ │ ├── stories │ │ │ ├── empty_state_container.tscn │ │ │ ├── ink_story_panel.tscn │ │ │ └── ink_story_configuration.gd │ │ ├── ink_bottom_panel.tscn │ │ ├── common │ │ │ ├── ink_progress_dialog.tscn │ │ │ ├── ink_progress_dialog.gd │ │ │ ├── ink_rich_dialog.tscn │ │ │ └── ink_rich_dialog.gd │ │ └── ink_bottom_panel.gd │ ├── import_plugins │ │ ├── ink_resource.gd │ │ ├── ink_source_import_plugin.gd │ │ └── ink_json_import_plugin.gd │ ├── common │ │ ├── executors │ │ │ ├── ink_external_command_executor.gd │ │ │ ├── structures │ │ │ │ ├── ink_execution_result.gd │ │ │ │ ├── ink_execution_configuration.gd │ │ │ │ └── ink_compilation_configuration.gd │ │ │ ├── ink_compiler.gd │ │ │ └── ink_configuration_tester.gd │ │ ├── ink_editor_interface.gd │ │ └── ink_csharp_validator.gd │ └── icons │ │ ├── compile.svg │ │ └── ink_player.svg │ ├── plugin.cfg │ ├── runtime │ ├── enums │ │ ├── error.gd │ │ └── push_pop.gd │ ├── values │ │ ├── value_type.gd │ │ ├── int_value.gd │ │ ├── float_value.gd │ │ ├── bool_value.gd │ │ ├── divert_target_value.gd │ │ ├── variable_pointer_value.gd │ │ ├── string_value.gd │ │ ├── list_value.gd │ │ └── value.gd │ ├── content │ │ ├── void.gd │ │ ├── glue.gd │ │ ├── tag.gd │ │ ├── choices │ │ │ ├── choice.gd │ │ │ └── choice_point.gd │ │ ├── variable_reference.gd │ │ ├── variable_assignment.gd │ │ └── divert.gd │ ├── extra │ │ ├── try_get_result.gd │ │ ├── function_result.gd │ │ ├── string_writer.gd │ │ ├── stopwatch.gd │ │ ├── story_error.gd │ │ ├── story_error_metadata.gd │ │ ├── key_value_pair.gd │ │ ├── state_element.gd │ │ └── string_set.gd │ ├── profiler.gd │ ├── common │ │ └── ink_base.gd │ ├── search_result.gd │ ├── debug_metadata.gd │ ├── lists │ │ ├── list_definitions_origin.gd │ │ ├── list_definition.gd │ │ └── structs │ │ │ └── ink_list_item.gd │ ├── state_patch.gd │ ├── structs │ │ └── pointer.gd │ └── flow.gd │ ├── LICENSE │ ├── runtime.gd │ └── ink_player_factory.gd ├── examples ├── images │ ├── inkgd.png │ ├── spinner.png │ ├── transparent.png │ ├── inkgd_the_intercept.png │ ├── buttons │ │ ├── button_normal.png │ │ ├── button_pressed.png │ │ ├── button_disabled.png │ │ ├── back_button_hover.png │ │ ├── back_button_normal.png │ │ ├── back_button_pressed.png │ │ ├── button_focus_hover.png │ │ ├── back_button_disabled.png │ │ ├── button_normal.png.import │ │ ├── button_pressed.png.import │ │ ├── button_disabled.png.import │ │ ├── back_button_hover.png.import │ │ ├── back_button_normal.png.import │ │ ├── back_button_pressed.png.import │ │ ├── button_focus_hover.png.import │ │ └── back_button_disabled.png.import │ ├── inkgd.png.import │ ├── spinner.png.import │ ├── transparent.png.import │ └── inkgd_the_intercept.png.import ├── fonts │ ├── Poppins-Medium.ttf │ ├── Vollkorn-Regular.ttf │ ├── Poppins-Medium.tres │ └── Vollkorn-Regular.tres ├── ink │ ├── not_a_halloween_game │ │ ├── includes │ │ │ ├── witch.ink │ │ │ ├── credits.ink │ │ │ ├── bob.ink.import │ │ │ ├── witch.ink.import │ │ │ ├── credits.ink.import │ │ │ ├── fishmen.ink.import │ │ │ ├── totem1.ink.import │ │ │ ├── totem2.ink.import │ │ │ ├── fishmen.ink │ │ │ ├── totem2.ink │ │ │ ├── totem1.ink │ │ │ └── bob.ink │ │ ├── not_a_halloween_game.ink.import │ │ ├── not_a_halloween_game.ink.json.import │ │ └── not_a_halloween_game.ink │ ├── ld41_emoji │ │ ├── ld41_emoji.ink.import │ │ └── ld41_emoji.ink.json.import │ ├── crime_scene │ │ ├── crime_scene.ink.import │ │ └── crime_scene.ink.json.import │ └── the_intercept │ │ ├── the_intercept.ink.import │ │ └── the_intercept.ink.json.import └── scenes │ ├── common │ ├── label.tscn │ ├── back_button_margin_container.gd │ ├── choice_container.tscn │ ├── button.tscn │ ├── back_button_margin_container.tscn │ ├── profiler.gd │ ├── choice_container.gd │ └── story_player.tscn │ ├── crime_scene.tscn │ ├── the_intercept.tscn │ └── main.gd └── ci_export_presets.cfg /addons/inkgd/mono/assemblies/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addons/inkgd/editor/templates/.gdignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/images/inkgd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/inkgd.png -------------------------------------------------------------------------------- /examples/images/spinner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/spinner.png -------------------------------------------------------------------------------- /examples/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/transparent.png -------------------------------------------------------------------------------- /examples/fonts/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/fonts/Poppins-Medium.ttf -------------------------------------------------------------------------------- /examples/fonts/Vollkorn-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/fonts/Vollkorn-Regular.ttf -------------------------------------------------------------------------------- /examples/images/inkgd_the_intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/inkgd_the_intercept.png -------------------------------------------------------------------------------- /examples/images/buttons/button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/button_normal.png -------------------------------------------------------------------------------- /examples/images/buttons/button_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/button_pressed.png -------------------------------------------------------------------------------- /examples/images/buttons/button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/button_disabled.png -------------------------------------------------------------------------------- /examples/images/buttons/back_button_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/back_button_hover.png -------------------------------------------------------------------------------- /examples/images/buttons/back_button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/back_button_normal.png -------------------------------------------------------------------------------- /examples/images/buttons/back_button_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/back_button_pressed.png -------------------------------------------------------------------------------- /examples/images/buttons/button_focus_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/button_focus_hover.png -------------------------------------------------------------------------------- /examples/images/buttons/back_button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ephread/inkgd/HEAD/examples/images/buttons/back_button_disabled.png -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/witch.ink: -------------------------------------------------------------------------------- 1 | 2 | 3 | == witch 4 | #showdialog 5 | {bob.save_again: What did you do! | Get out of my way peon...} 6 | 7 | -> explore_map -------------------------------------------------------------------------------- /addons/inkgd/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="InkGD" 4 | description="Full implementation of inkle's Ink narrative language in pure GDScript, with editor support." 5 | author="Frédéric Maquin" 6 | version="0.5.0" 7 | script="editor/ink_editor_plugin.gd" 8 | -------------------------------------------------------------------------------- /examples/fonts/Poppins-Medium.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://examples/fonts/Poppins-Medium.ttf" type="DynamicFontData" id=1] 4 | 5 | [resource] 6 | size = 32 7 | font_data = ExtResource( 1 ) 8 | -------------------------------------------------------------------------------- /examples/fonts/Vollkorn-Regular.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [sub_resource type="DynamicFontData" id=1] 4 | font_path = "res://examples/fonts/Vollkorn-Regular.ttf" 5 | 6 | [resource] 7 | size = 32 8 | font_data = SubResource( 1 ) 9 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/credits.ink: -------------------------------------------------------------------------------- 1 | 2 | = credits 3 | #showdialog #fadetoblack #credits 4 | 5 | Written by Samuel Sarette 6 | Software by Zach & Sam Sarette 7 | Art & Music by Zach Sarette 8 | Fog Shader by Gonkee (Youtuber, Open-Source) 9 | 10 | -> END -------------------------------------------------------------------------------- /examples/ink/ld41_emoji/ld41_emoji.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/ld41_emoji.ink-c4e1757539a0b377b87783219adaba47.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/ld41_emoji/ld41_emoji.ink" 10 | dest_files=[ "res://.import/ld41_emoji.ink-c4e1757539a0b377b87783219adaba47.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/crime_scene/crime_scene.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/crime_scene.ink-d23408247328262b91338c538eda77b4.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/crime_scene/crime_scene.ink" 10 | dest_files=[ "res://.import/crime_scene.ink-d23408247328262b91338c538eda77b4.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/bob.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/bob.ink-e0b0b22412a3d224ec629c3e67b547c0.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/bob.ink" 10 | dest_files=[ "res://.import/bob.ink-e0b0b22412a3d224ec629c3e67b547c0.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/witch.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/witch.ink-82ee5065daa0dd7393abaeabe1fd17d0.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/witch.ink" 10 | dest_files=[ "res://.import/witch.ink-82ee5065daa0dd7393abaeabe1fd17d0.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/the_intercept/the_intercept.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/the_intercept.ink-b926d86e1fd973cb5c283114aa9aaa2e.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/the_intercept/the_intercept.ink" 10 | dest_files=[ "res://.import/the_intercept.ink-b926d86e1fd973cb5c283114aa9aaa2e.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/credits.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/credits.ink-29cab6d86e1e3bb67c208658b89b1a80.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/credits.ink" 10 | dest_files=[ "res://.import/credits.ink-29cab6d86e1e3bb67c208658b89b1a80.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/fishmen.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/fishmen.ink-49f9c0f06bef2ff0a55df32728865421.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/fishmen.ink" 10 | dest_files=[ "res://.import/fishmen.ink-49f9c0f06bef2ff0a55df32728865421.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/totem1.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/totem1.ink-ac922e7bc02efd5052560ee3e8d24539.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/totem1.ink" 10 | dest_files=[ "res://.import/totem1.ink-ac922e7bc02efd5052560ee3e8d24539.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/totem2.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/totem2.ink-3c698c351ed5f1ffe5e1ffe993ec6788.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/includes/totem2.ink" 10 | dest_files=[ "res://.import/totem2.ink-3c698c351ed5f1ffe5e1ffe993ec6788.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/ld41_emoji/ld41_emoji.ink.json.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink.json" 4 | type="Resource" 5 | path="res://.import/ld41_emoji.ink.json-13fdb4ae8f6b7b82055e18ba9228b40a.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/ld41_emoji/ld41_emoji.ink.json" 10 | dest_files=[ "res://.import/ld41_emoji.ink.json-13fdb4ae8f6b7b82055e18ba9228b40a.res" ] 11 | 12 | [params] 13 | 14 | compress=true 15 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/stories/empty_state_container.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="EmptyStateContainer" type="CenterContainer"] 4 | margin_right = 1902.0 5 | margin_bottom = 100.0 6 | rect_min_size = Vector2( 0, 100 ) 7 | 8 | [node name="Label" type="Label" parent="."] 9 | margin_left = 879.0 10 | margin_top = 43.0 11 | margin_right = 1022.0 12 | margin_bottom = 57.0 13 | text = "No stories to compile." 14 | -------------------------------------------------------------------------------- /examples/ink/crime_scene/crime_scene.ink.json.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink.json" 4 | type="Resource" 5 | path="res://.import/crime_scene.ink.json-be8f50d4761996692222dbbc16a649d5.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/crime_scene/crime_scene.ink.json" 10 | dest_files=[ "res://.import/crime_scene.ink.json-be8f50d4761996692222dbbc16a649d5.res" ] 11 | 12 | [params] 13 | 14 | compress=true 15 | -------------------------------------------------------------------------------- /examples/scenes/common/label.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://examples/fonts/Vollkorn-Regular.tres" type="DynamicFont" id=1] 4 | 5 | [node name="Label" type="Label"] 6 | margin_top = 1.94104 7 | margin_right = 77.0 8 | margin_bottom = 47.941 9 | custom_fonts/font = ExtResource( 1 ) 10 | custom_colors/font_color = Color( 0, 0, 0, 1 ) 11 | text = "Label" 12 | align = 1 13 | autowrap = true 14 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/not_a_halloween_game.ink.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink" 4 | type="Resource" 5 | path="res://.import/not_a_halloween_game.ink-45d46180744879f71621c25fa12241b0.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/not_a_halloween_game.ink" 10 | dest_files=[ "res://.import/not_a_halloween_game.ink-45d46180744879f71621c25fa12241b0.res" ] 11 | 12 | [params] 13 | 14 | -------------------------------------------------------------------------------- /examples/ink/the_intercept/the_intercept.ink.json.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink.json" 4 | type="Resource" 5 | path="res://.import/the_intercept.ink.json-95c119299f1caf91fdc3f4547e6e8121.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/the_intercept/the_intercept.ink.json" 10 | dest_files=[ "res://.import/the_intercept.ink.json-95c119299f1caf91fdc3f4547e6e8121.res" ] 11 | 12 | [params] 13 | 14 | compress=true 15 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/not_a_halloween_game.ink.json.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="inkgd.ink.json" 4 | type="Resource" 5 | path="res://.import/not_a_halloween_game.ink.json-d901917e9d05208c13c92292b34747e2.res" 6 | 7 | [deps] 8 | 9 | source_file="res://examples/ink/not_a_halloween_game/not_a_halloween_game.ink.json" 10 | dest_files=[ "res://.import/not_a_halloween_game.ink.json-d901917e9d05208c13c92292b34747e2.res" ] 11 | 12 | [params] 13 | 14 | compress=true 15 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/enums/error.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | 11 | enum ErrorType { 12 | AUTHOR, 13 | WARNING, 14 | ERROR 15 | } 16 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/enums/push_pop.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | 11 | enum PushPopType { 12 | TUNNEL, 13 | FUNCTION, 14 | FUNCTION_EVALUATION_FROM_GAME 15 | } 16 | -------------------------------------------------------------------------------- /examples/scenes/common/back_button_margin_container.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2018-2022 Paul Joannon 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # Licensed under the MIT License. 5 | # See LICENSE in the project root for license information. 6 | # ############################################################################ # 7 | 8 | extends MarginContainer 9 | 10 | func _switch_to_main(): 11 | get_tree().change_scene("res://examples/scenes/main.tscn") 12 | -------------------------------------------------------------------------------- /examples/scenes/common/choice_container.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://examples/scenes/common/choice_container.gd" type="Script" id=1] 4 | 5 | [node name="ChoiceContainer" type="MarginContainer"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | custom_constants/margin_right = 150 9 | custom_constants/margin_left = 150 10 | script = ExtResource( 1 ) 11 | __meta__ = { 12 | "_edit_use_anchors_": false 13 | } 14 | 15 | [node name="ChoiceVBoxContainer" type="VBoxContainer" parent="."] 16 | margin_left = 150.0 17 | margin_right = 1770.0 18 | margin_bottom = 1080.0 19 | custom_constants/separation = 10 20 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/value_type.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | 13 | enum ValueType { 14 | BOOL = -1, 15 | 16 | INT, 17 | FLOAT, 18 | LIST, 19 | STRING, 20 | 21 | DIVERT_TARGET, 22 | VARIABLE_POINTER 23 | } 24 | -------------------------------------------------------------------------------- /addons/inkgd/editor/import_plugins/ink_resource.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Resource 8 | 9 | # A very simple resource to store the content of a json file, as a string. 10 | class_name InkResource 11 | 12 | # ############################################################################ # 13 | # Properties 14 | # ############################################################################ # 15 | 16 | export(String) var json: String = "" 17 | -------------------------------------------------------------------------------- /examples/scenes/crime_scene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://examples/scenes/common/story_player.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://examples/scenes/common/back_button_margin_container.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://examples/ink/crime_scene/crime_scene.ink.json" type="Resource" id=3] 6 | 7 | [node name="CrimeScene" type="Control"] 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | __meta__ = { 11 | "_edit_use_anchors_": false 12 | } 13 | 14 | [node name="StoryPlayer" parent="." instance=ExtResource( 1 )] 15 | ink_file = ExtResource( 3 ) 16 | title = "Crime Scene" 17 | bind_externals = false 18 | 19 | [node name="BackButtonMarginContainer" parent="." instance=ExtResource( 2 )] 20 | -------------------------------------------------------------------------------- /examples/scenes/the_intercept.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://examples/ink/the_intercept/the_intercept.ink.json" type="Resource" id=1] 4 | [ext_resource path="res://examples/scenes/common/story_player.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://examples/scenes/common/back_button_margin_container.tscn" type="PackedScene" id=3] 6 | 7 | [node name="TheIntercept" type="Control"] 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | margin_left = 1.0 11 | margin_right = 1.0 12 | __meta__ = { 13 | "_edit_use_anchors_": false 14 | } 15 | 16 | [node name="StoryPlayer" parent="." instance=ExtResource( 2 )] 17 | ink_file = ExtResource( 1 ) 18 | title = "The Intercept" 19 | 20 | [node name="BackButtonMarginContainer" parent="." instance=ExtResource( 3 )] 21 | -------------------------------------------------------------------------------- /examples/scenes/main.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Control 8 | 9 | func _ready(): 10 | pass 11 | 12 | func _switch_to_the_intercept(): 13 | get_tree().change_scene("res://examples/scenes/the_intercept.tscn") 14 | 15 | func _switch_to_crime_scene(): 16 | get_tree().change_scene("res://examples/scenes/crime_scene.tscn") 17 | 18 | func _switch_to_performance(): 19 | get_tree().change_scene("res://test/performance/performance_test.tscn") 20 | 21 | func _switch_to_gut(): 22 | get_tree().change_scene("res://test/tests.tscn") 23 | -------------------------------------------------------------------------------- /examples/images/inkgd.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/inkgd.png-7b7b5f8dcc67c2eca2fbc207798880a3.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/inkgd.png" 13 | dest_files=[ "res://.import/inkgd.png-7b7b5f8dcc67c2eca2fbc207798880a3.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/spinner.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/spinner.png-08a546a534683b20a49d773181d984bd.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/spinner.png" 13 | dest_files=[ "res://.import/spinner.png-08a546a534683b20a49d773181d984bd.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/void.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | extends InkObject 11 | 12 | class_name InkVoid 13 | 14 | # ############################################################################ # 15 | # GDScript extra methods 16 | # ############################################################################ # 17 | 18 | func is_class(type: String) -> bool: 19 | return type == "Void" || .is_class(type) 20 | 21 | func get_class() -> String: 22 | return "Void" 23 | 24 | func _to_string() -> String: 25 | return "Void" 26 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/try_get_result.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | 12 | extends Reference 13 | 14 | class_name InkTryGetResult 15 | 16 | # ############################################################################ # 17 | 18 | var exists: bool = false # Bool 19 | var result = null # Variant 20 | 21 | # ############################################################################ # 22 | 23 | func _init(exists: bool, result): 24 | self.exists = exists 25 | self.result = result 26 | -------------------------------------------------------------------------------- /examples/images/transparent.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/transparent.png-da1b45d02dd552ad2a0720be3e2dd76b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/transparent.png" 13 | dest_files=[ "res://.import/transparent.png-da1b45d02dd552ad2a0720be3e2dd76b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/function_result.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | extends Reference 12 | 13 | class_name InkFunctionResult 14 | 15 | # ############################################################################ # 16 | 17 | var text_output: String = "" 18 | var return_value = null 19 | 20 | # ############################################################################ # 21 | 22 | func _init(text_output: String, return_value): 23 | self.text_output = text_output 24 | self.return_value = return_value 25 | -------------------------------------------------------------------------------- /examples/images/buttons/button_normal.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/button_normal.png-9821078320ab3324dc046b9fb639a46d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/button_normal.png" 13 | dest_files=[ "res://.import/button_normal.png-9821078320ab3324dc046b9fb639a46d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/button_pressed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/button_pressed.png-d343f73f9a1cc7fece22dd3de8602d1c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/button_pressed.png" 13 | dest_files=[ "res://.import/button_pressed.png-d343f73f9a1cc7fece22dd3de8602d1c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/button_disabled.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/button_disabled.png-6819ad0127fe4a268fafa369245316e5.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/button_disabled.png" 13 | dest_files=[ "res://.import/button_disabled.png-6819ad0127fe4a268fafa369245316e5.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/inkgd_the_intercept.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/inkgd_the_intercept.png-3b24c1f96a2cf5610bbbfe6d94ac5d81.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/inkgd_the_intercept.png" 13 | dest_files=[ "res://.import/inkgd_the_intercept.png-3b24c1f96a2cf5610bbbfe6d94ac5d81.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/back_button_hover.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/back_button_hover.png-db34cb7825dc46b4690b9b405d33ee93.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/back_button_hover.png" 13 | dest_files=[ "res://.import/back_button_hover.png-db34cb7825dc46b4690b9b405d33ee93.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/back_button_normal.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/back_button_normal.png-dea0719a57fa24dd26cdd04cfeb4fca0.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/back_button_normal.png" 13 | dest_files=[ "res://.import/back_button_normal.png-dea0719a57fa24dd26cdd04cfeb4fca0.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/back_button_pressed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/back_button_pressed.png-bc98095c48f4e6d5dc6f3d6e78f4925f.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/back_button_pressed.png" 13 | dest_files=[ "res://.import/back_button_pressed.png-bc98095c48f4e6d5dc6f3d6e78f4925f.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/button_focus_hover.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/button_focus_hover.png-fe82460be0025c25effb77d0d5669cfb.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/button_focus_hover.png" 13 | dest_files=[ "res://.import/button_focus_hover.png-fe82460be0025c25effb77d0d5669cfb.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /examples/images/buttons/back_button_disabled.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/back_button_disabled.png-7cc8f448bef6cff13130d078b7bfaa86.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://examples/images/buttons/back_button_disabled.png" 13 | dest_files=[ "res://.import/back_button_disabled.png-7cc8f448bef6cff13130d078b7bfaa86.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/glue.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | extends InkObject 11 | 12 | class_name InkGlue 13 | 14 | # ############################################################################ # 15 | 16 | func _to_string() -> String: 17 | return "Glue" 18 | 19 | # ############################################################################ # 20 | # GDScript extra methods 21 | # ############################################################################ # 22 | 23 | func is_class(type: String) -> bool: 24 | return type == "Glue" || .is_class(type) 25 | 26 | func get_class() -> String: 27 | return "Glue" 28 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/tag.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | extends InkObject 11 | 12 | class_name InkTag 13 | 14 | var text: String 15 | 16 | func _init(tag_text: String): 17 | text = tag_text 18 | 19 | func _to_string() -> String: 20 | return '# ' + text 21 | 22 | # ############################################################################ # 23 | # GDScript extra methods 24 | # ############################################################################ # 25 | 26 | func is_class(type: String) -> bool: 27 | return type == "Tag" || .is_class(type) 28 | 29 | func get_class() -> String: 30 | return "Tag" 31 | -------------------------------------------------------------------------------- /ci_export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="HTML5" 4 | platform="HTML5" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | export_path="../inkgd.html" 11 | script_export_mode=1 12 | script_encryption_key="" 13 | 14 | [preset.0.options] 15 | 16 | custom_template/debug="" 17 | custom_template/release="" 18 | variant/export_type=0 19 | vram_texture_compression/for_desktop=true 20 | vram_texture_compression/for_mobile=false 21 | html/export_icon=true 22 | html/custom_html_shell="" 23 | html/head_include="" 24 | html/canvas_resize_policy=2 25 | html/focus_canvas_on_start=true 26 | html/experimental_virtual_keyboard=false 27 | progressive_web_app/enabled=false 28 | progressive_web_app/offline_page="" 29 | progressive_web_app/display=1 30 | progressive_web_app/orientation=0 31 | progressive_web_app/icon_144x144="" 32 | progressive_web_app/icon_180x180="" 33 | progressive_web_app/icon_512x512="" 34 | progressive_web_app/background_color=Color( 0, 0, 0, 1 ) 35 | -------------------------------------------------------------------------------- /examples/scenes/common/button.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=2] 2 | 3 | [ext_resource path="res://examples/fonts/Vollkorn-Regular.tres" type="DynamicFont" id=1] 4 | 5 | [sub_resource type="StyleBoxEmpty" id=1] 6 | 7 | [sub_resource type="StyleBoxEmpty" id=2] 8 | 9 | [sub_resource type="StyleBoxEmpty" id=3] 10 | 11 | [sub_resource type="StyleBoxEmpty" id=4] 12 | 13 | [sub_resource type="StyleBoxEmpty" id=5] 14 | 15 | [node name="Button" type="Button"] 16 | margin_right = 75.0 17 | margin_bottom = 20.0 18 | custom_styles/hover = SubResource( 1 ) 19 | custom_styles/pressed = SubResource( 2 ) 20 | custom_styles/focus = SubResource( 3 ) 21 | custom_styles/disabled = SubResource( 4 ) 22 | custom_styles/normal = SubResource( 5 ) 23 | custom_fonts/font = ExtResource( 1 ) 24 | custom_colors/font_color_disabled = Color( 0, 0, 0, 1 ) 25 | custom_colors/font_color = Color( 0.541176, 0.34902, 0.141176, 1 ) 26 | custom_colors/font_color_hover = Color( 0, 0, 0, 1 ) 27 | custom_colors/font_color_pressed = Color( 0, 0, 0, 1 ) 28 | text = "Button" 29 | flat = true 30 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/string_writer.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:unused_class_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | # Simple replacement of the StringWriter class from the .NET Framework. 12 | # It has none of the optimisations of original class and merely wraps 13 | # a plain old string. 14 | 15 | 16 | class_name InkStringWriter 17 | 18 | # ############################################################################ # 19 | 20 | var _internal_string: String = "" 21 | 22 | # ############################################################################ # 23 | 24 | func _init(): 25 | pass 26 | 27 | # ############################################################################ # 28 | 29 | func write(s: String) -> void: 30 | _internal_string += str(s) 31 | 32 | func _to_string() -> String: 33 | return _internal_string 34 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/stopwatch.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:unused_class_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | # Simple replacement of the Stopwatch class from the .NET Framework. 12 | # Less accurate than the original implemntation, but good enough for 13 | # the use-case. 14 | 15 | class_name InkStopWatch 16 | 17 | # ############################################################################ # 18 | 19 | var _start_time: int = -1 20 | 21 | var elapsed_milliseconds setget , get_elapsed_milliseconds 22 | func get_elapsed_milliseconds() -> int: 23 | if _start_time == -1: 24 | return 0 25 | 26 | return OS.get_ticks_msec() - _start_time 27 | 28 | # ############################################################################ # 29 | 30 | func start() -> void: 31 | _start_time = OS.get_ticks_msec() 32 | 33 | func stop() -> void: 34 | _start_time = -1 35 | -------------------------------------------------------------------------------- /addons/inkgd/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 inkle Ltd. 4 | Copyright (c) 2018-2021 Paul Joannon 5 | Copyright (c) 2019-2022 Frédéric Maquin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/story_error.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | # An object tha represents a "Story Error", which is equivalent in certain 11 | # context to upstream's StoryException. 12 | 13 | class_name StoryError 14 | 15 | # ############################################################################ # 16 | # Properties 17 | # ############################################################################ # 18 | 19 | var message: String 20 | var use_end_line_number: bool 21 | var metadata # StoryErrorMetadata | null 22 | 23 | # ############################################################################ # 24 | # Initialization 25 | # ############################################################################ # 26 | 27 | func _init(message: String, use_end_line_number: bool, metadata): 28 | self.message = message 29 | self.use_end_line_number = use_end_line_number 30 | self.metadata = metadata 31 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/profiler.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | extends InkBase 11 | 12 | class_name InkProfiler 13 | 14 | func _init(): 15 | pass 16 | 17 | # () -> String 18 | func report() -> String: 19 | return "" 20 | 21 | # () -> void 22 | func pre_continue() -> void: 23 | pass 24 | 25 | # () -> void 26 | func post_continue() -> void: 27 | pass 28 | 29 | # () -> void 30 | func pre_step() -> void: 31 | pass 32 | 33 | # (CallStack) -> void 34 | func step(callstack: InkCallStack) -> void: 35 | pass 36 | 37 | # () -> void 38 | func post_step() -> void: 39 | pass 40 | 41 | func step_length_record() -> String: 42 | return "" 43 | 44 | func mega_log() -> String: 45 | return "" 46 | 47 | func pre_snapshot() -> void: 48 | pass 49 | 50 | func post_snapshot() -> void: 51 | pass 52 | 53 | func millisecs(watch: InkStopWatch) -> float: 54 | return 0.0 55 | 56 | static func format_millisecs(num: float) -> String: 57 | return "" 58 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/ink_external_command_executor.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Reference 8 | 9 | class_name InkExternalCommandExecutor 10 | 11 | # ############################################################################ # 12 | # Properties 13 | # ############################################################################ # 14 | 15 | ## The identifier of this compiler. 16 | var identifier: int setget , get_identifier 17 | func get_identifier() -> int: 18 | return get_instance_id() 19 | 20 | # ############################################################################ # 21 | # Constants 22 | # ############################################################################ # 23 | 24 | const BOM = "\ufeff" 25 | 26 | # ############################################################################ # 27 | # Private Properties 28 | # ############################################################################ # 29 | 30 | ## Thread used to compile the story. 31 | var _thread: Thread 32 | -------------------------------------------------------------------------------- /addons/inkgd/runtime.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # All Rights Reserved 4 | # 5 | # This file is part of inkgd. 6 | # inkgd is licensed under the terms of the MIT license. 7 | # ############################################################################ # 8 | 9 | extends Node 10 | 11 | # Hiding this type to prevent registration of "private" nodes. 12 | # See https://github.com/godotengine/godot-proposals/issues/1047 13 | # class_name InkRuntime 14 | 15 | static func init(root_node, stop_on_error = true): 16 | if root_node.has_node("__InkRuntime"): 17 | var _ink_runtime = root_node.get_node("__InkRuntime") 18 | _ink_runtime.stop_execution_on_exception = stop_on_error 19 | _ink_runtime.stop_execution_on_error = stop_on_error 20 | 21 | return _ink_runtime 22 | 23 | var _ink_runtime = load("res://addons/inkgd/runtime/static/ink_runtime.gd").new() 24 | 25 | _ink_runtime.stop_execution_on_exception = stop_on_error 26 | _ink_runtime.stop_execution_on_error = stop_on_error 27 | 28 | root_node.add_child(_ink_runtime) 29 | 30 | return _ink_runtime 31 | 32 | static func deinit(root_node): 33 | var _ink_runtime = root_node.get_node("__InkRuntime") 34 | root_node.remove_child(_ink_runtime) 35 | _ink_runtime.queue_free() 36 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/common/ink_base.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends Reference 13 | 14 | class_name InkBase 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | var Utils := preload("res://addons/inkgd/runtime/extra/utils.gd") as GDScript 21 | 22 | # ############################################################################ # 23 | 24 | func equals(ink_base) -> bool: 25 | return false 26 | 27 | 28 | # ############################################################################ # 29 | # GDScript extra methods 30 | # ############################################################################ # 31 | 32 | func is_class(type: String) -> bool: 33 | return type == "InkBase" || .is_class(type) 34 | 35 | func get_class() -> String: 36 | return "InkBase" 37 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/story_error_metadata.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | # An object that keeps track of the Debug Metadata and current pointer at the 11 | # exact moment an error was raised, so that they can be processed and reported 12 | # later. It's required because GDScript doesn't support exceptions and 13 | # errors don't bubble up the stack. 14 | 15 | class_name StoryErrorMetadata 16 | 17 | # ############################################################################ # 18 | # Properties 19 | # ############################################################################ # 20 | 21 | var debug_metadata # InkDebugMetadata | null 22 | var pointer: InkPointer 23 | 24 | # ############################################################################ # 25 | # Initialization 26 | # ############################################################################ # 27 | 28 | func _init(debug_metadata: InkDebugMetadata, pointer: InkPointer): 29 | self.debug_metadata = debug_metadata 30 | self.pointer = pointer 31 | -------------------------------------------------------------------------------- /addons/inkgd/editor/import_plugins/ink_source_import_plugin.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2018-2022 Paul Joannon 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # Licensed under the MIT License. 5 | # See LICENSE in the project root for license information. 6 | # ############################################################################ # 7 | 8 | extends EditorImportPlugin 9 | 10 | class_name InkSourceImportPlugin 11 | 12 | # ############################################################################ # 13 | # Overrides 14 | # ############################################################################ # 15 | 16 | func get_importer_name(): 17 | return "inkgd.ink"; 18 | 19 | func get_visible_name(): 20 | return "Ink file"; 21 | 22 | func get_recognized_extensions(): 23 | return ["ink"]; 24 | 25 | func get_save_extension(): 26 | return "res"; 27 | 28 | func get_resource_type(): 29 | return "Resource"; 30 | 31 | func get_import_options(preset): 32 | return [] 33 | 34 | func get_option_visibility(option, options): 35 | return true 36 | 37 | func get_preset_count(): 38 | return 0 39 | 40 | func import(source_file, save_path, options, r_platform_variants, r_gen_files): 41 | return ResourceSaver.save( 42 | "%s.%s" % [save_path, get_save_extension()], 43 | Resource.new(), 44 | ResourceSaver.FLAG_COMPRESS 45 | ) 46 | -------------------------------------------------------------------------------- /examples/scenes/common/back_button_margin_container.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://examples/images/buttons/back_button_disabled.png" type="Texture" id=1] 4 | [ext_resource path="res://examples/images/buttons/back_button_normal.png" type="Texture" id=2] 5 | [ext_resource path="res://examples/images/buttons/back_button_pressed.png" type="Texture" id=3] 6 | [ext_resource path="res://examples/images/buttons/back_button_hover.png" type="Texture" id=4] 7 | [ext_resource path="res://examples/scenes/common/back_button_margin_container.gd" type="Script" id=5] 8 | 9 | [node name="BackButtonMarginContainer" type="MarginContainer"] 10 | margin_right = 64.0 11 | margin_bottom = 64.0 12 | custom_constants/margin_right = 75 13 | custom_constants/margin_top = 75 14 | custom_constants/margin_left = 75 15 | custom_constants/margin_bottom = 75 16 | script = ExtResource( 5 ) 17 | __meta__ = { 18 | "_edit_use_anchors_": false 19 | } 20 | 21 | [node name="TextureButton" type="TextureButton" parent="."] 22 | margin_left = 75.0 23 | margin_top = 75.0 24 | margin_right = 139.0 25 | margin_bottom = 139.0 26 | texture_normal = ExtResource( 2 ) 27 | texture_pressed = ExtResource( 3 ) 28 | texture_hover = ExtResource( 4 ) 29 | texture_disabled = ExtResource( 1 ) 30 | texture_focused = ExtResource( 2 ) 31 | __meta__ = { 32 | "_edit_use_anchors_": false 33 | } 34 | 35 | [connection signal="pressed" from="TextureButton" to="." method="_switch_to_main"] 36 | -------------------------------------------------------------------------------- /examples/scenes/common/profiler.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Reference 8 | 9 | class_name InkGDProfiler 10 | 11 | # ############################################################################ # 12 | # Properties 13 | # ############################################################################ # 14 | 15 | var milliseconds_elaspsed: int setget , get_milliseconds_elaspsed 16 | func get_milliseconds_elaspsed(): 17 | if _start_time == -1 || _end_time == -1: 18 | return 0 19 | 20 | return _end_time - _start_time 21 | 22 | 23 | # ############################################################################ # 24 | # Private properties 25 | # ############################################################################ # 26 | 27 | var _start_time: int = -1 28 | var _end_time: int = -1 29 | 30 | 31 | # ############################################################################ # 32 | # Methods 33 | # ############################################################################ # 34 | 35 | func start(): 36 | _start_time = OS.get_ticks_msec() 37 | 38 | 39 | func stop(): 40 | _end_time = OS.get_ticks_msec() 41 | 42 | 43 | func reset(): 44 | _start_time = 0 45 | _end_time = 0 46 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/fishmen.ink: -------------------------------------------------------------------------------- 1 | 2 | 3 | == fishman1 4 | #showdialog 5 | Fishbert: Hey, do you mind if we hang around for a while? 6 | + [Why not?] -> freindly 7 | + Go [Away!] back to the depths from whence you came! 8 | -> hostile 9 | 10 | = freindly 11 | Fishbert: Radical! Thanks dude. 12 | Fishbert: Hey is that Loretta witch bothering you? 13 | + Very Much 14 | + Yes 15 | + Wow is she ever 16 | + Who isn't bothered by her? 17 | - 18 | Fishbert: That's what I thought. She's been a pain in our fins: 19 | Fishbert: Do this, destroy that, come on land. Wow, demanding much? 20 | Fishbert: We'll take care of her for ya. 21 | (destroy hotel cutscene) #hidedialog #cutscene:destroy_hotel #contshowdialog:5 22 | #fadetoblack 23 | -> freindly_consequence 24 | 25 | = freindly_consequence 26 | #music:stranded 27 | Bob: You don't mind if I crash on your couch for a while? 28 | + No not at all 29 | Bob: Great. All my stuff was in that hotel, but at least I'm free of that witch. 30 | And the town is safe. 31 | (Good End) 32 | + Wow, I don't really have a big apartment... 33 | Don't be a jerk, player 34 | -> freindly_consequence 35 | - 36 | - -> credits 37 | 38 | = hostile 39 | #music:stranded 40 | Fishbert: Wow, what a jerk. 41 | (destroy town cutscene) #hidedialog #cutscene:destroy_town #contshowdialog:7 42 | Bob: Let's get out of this town! #showdialog #fadetoblack 43 | I guess we'll grab the last boat out of here and part ways... 44 | (Bad End) 45 | -> credits -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/ink_bottom_panel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/inkgd/editor/panel/ink_bottom_panel.gd" type="Script" id=1] 4 | 5 | [node name="Control" type="Control"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | script = ExtResource( 1 ) 9 | __meta__ = { 10 | "_edit_use_anchors_": false 11 | } 12 | 13 | [node name="TabContainer" type="TabContainer" parent="."] 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | rect_min_size = Vector2( 0, 428.75 ) 17 | size_flags_vertical = 3 18 | tab_align = 0 19 | drag_to_rearrange_enabled = true 20 | __meta__ = { 21 | "_edit_use_anchors_": false 22 | } 23 | 24 | [node name="MarginContainer" type="MarginContainer" parent="."] 25 | anchor_right = 1.0 26 | margin_bottom = 24.0 27 | rect_clip_content = true 28 | mouse_filter = 2 29 | size_flags_horizontal = 0 30 | size_flags_vertical = 0 31 | custom_constants/margin_right = 5 32 | custom_constants/margin_top = 5 33 | custom_constants/margin_left = 5 34 | custom_constants/margin_bottom = 5 35 | __meta__ = { 36 | "_edit_use_anchors_": false 37 | } 38 | 39 | [node name="LinkButton" type="LinkButton" parent="MarginContainer"] 40 | self_modulate = Color( 1, 1, 1, 0.647059 ) 41 | margin_left = 1887.0 42 | margin_top = 5.0 43 | margin_right = 1915.0 44 | margin_bottom = 19.0 45 | hint_tooltip = "Open a new issue on Github to report a problem!" 46 | size_flags_horizontal = 8 47 | text = "beta" 48 | underline = 1 49 | __meta__ = { 50 | "_edit_use_anchors_": false 51 | } 52 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/structures/ink_execution_result.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Reference 8 | 9 | ## A test result, containing information about whether the test 10 | ## suceeded and the generated output. 11 | class_name InkExecutionResult 12 | 13 | # ############################################################################ # 14 | # Properties 15 | # ############################################################################ # 16 | 17 | ## The identifier of the compiler that generated this result. 18 | ## This is the value of 'InkExecutor.identifier'. 19 | var identifier: int = 0 20 | 21 | var use_threads: bool = false 22 | var user_triggered: bool = false 23 | 24 | var success: bool = false 25 | 26 | var output: String = "" 27 | 28 | # ############################################################################ # 29 | # Overrides 30 | # ############################################################################ # 31 | 32 | func _init( 33 | identifier: int, 34 | use_threads: bool, 35 | user_triggered: bool, 36 | success: bool, 37 | output: String 38 | ): 39 | self.identifier = identifier 40 | self.use_threads = use_threads 41 | self.user_triggered = user_triggered 42 | self.success = success 43 | self.output = output 44 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/totem2.ink: -------------------------------------------------------------------------------- 1 | 2 | 3 | == totem2 4 | #showdialog 5 | { 6 | - bob.chase: -> talk_to 7 | - else: 8 | That's strange, I wonder what this thing is? 9 | -> explore_map 10 | } 11 | 12 | = talk_to 13 | #showdialog 14 | { 15 | - totem1: 16 | {totem1.wrong: Hey now, if you thought the other one was hard, watch out for me! Dare you? | Scared of us? Want to try me? } 17 | - else: 18 | {totem2.wrong: Hahaha back for more?} Do you dare disturb me? 19 | } 20 | + [Sure do] 21 | + [No, sorry, nevermind] -> explore_map 22 | - 23 | Laura’s mother has four children: Stewart, Ursula, Rain and… 24 | * Nicole -> wrong 25 | + Laura 26 | * Alexandria -> wrong 27 | * Ollie -> wrong 28 | - 29 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 30 | 31 | What would you call something that paces back and forth on the ocean floor? 32 | * A crab -> wrong 33 | + A nervous wreck 34 | * A treasure map -> wrong 35 | * A confused shark -> wrong 36 | - 37 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 38 | 39 | If you throw a golden fishing rod into a blue sea what will it be 40 | * Green -> wrong 41 | + Wet 42 | * Brown -> wrong 43 | * Cold -> wrong 44 | - 45 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 46 | 47 | -> defeat 48 | 49 | = defeat 50 | No! How could you! #defeat:totem2 51 | {totem1.defeat: -> bob.last_totem } 52 | 53 | -> explore_map 54 | 55 | = wrong 56 | Go away kid, come back when you're serious about word-play and lateral thinking. 57 | 58 | -> explore_map -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/key_value_pair.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | extends Reference 12 | 13 | class_name InkKeyValuePair 14 | 15 | # ############################################################################ # 16 | # Self-reference 17 | # ############################################################################ # 18 | 19 | static func InkKeyValuePair() -> GDScript: 20 | return load("res://addons/inkgd/runtime/extra/key_value_pair.gd") as GDScript 21 | 22 | # ############################################################################ # 23 | 24 | var key = null 25 | var value = null 26 | 27 | # ############################################################################ # 28 | 29 | func _init(): 30 | pass 31 | 32 | func _init_with_key_value(key, value): 33 | self.key = key 34 | self.value = value 35 | 36 | func _to_string(): 37 | return ("[KeyValuePair (%s, %s)]" % [key, value]) 38 | 39 | # ############################################################################ # 40 | 41 | static func new_with_key_value(key, value) -> InkKeyValuePair: 42 | var key_value_pair = InkKeyValuePair().new() 43 | key_value_pair._init_with_key_value(key, value) 44 | 45 | return key_value_pair 46 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/not_a_halloween_game.ink: -------------------------------------------------------------------------------- 1 | /* 2 | Main Project File 3 | */ 4 | INCLUDE includes/bob.ink 5 | INCLUDE includes/totem1.ink 6 | INCLUDE includes/totem2.ink 7 | INCLUDE includes/witch.ink 8 | INCLUDE includes/fishmen.ink 9 | INCLUDE includes/credits.ink 10 | 11 | VAR PlayerX = 0 12 | VAR PlayerY = 0 13 | /* This is a comment */ 14 | -> start 15 | 16 | == start 17 | #music:running 18 | #showdialog 19 | It's the night before Halloween. I've gotta do some work. 20 | Jared said there was a fire to put out in the graveyard. *sigh* 21 | Damn hooligans... 22 | 23 | -> explore_map 24 | 25 | == explore_map 26 | #music:sad 27 | #hidedialog 28 | #safe_to_save 29 | Navigate around the world... 30 | + [Jared] 31 | #showdialog 32 | {fire1 and fire2: {~Thanks for taking care of those fires.|What's up? Oh, the brink of destruction you say? Crazy.} | Hey, the graveyard is still burning guy...} 33 | -> explore_map 34 | * [Fire1] -> fire1 35 | * [Fire2] -> fire2 36 | + [Bob] -> bob 37 | + {not totem1.defeat} [Totem1] -> totem1 38 | + {not totem2.defeat} [Totem2] -> totem2 39 | + [Witch] -> witch 40 | + {bob.save_again} [Fishman1] -> fishman1 41 | - 42 | 43 | -> explore_map 44 | 45 | = fire1 46 | #showdialog 47 | There. That's the {explore_map.fire2: second} fire put out... 48 | -> post_fire 49 | 50 | = fire2 51 | #showdialog 52 | Who would vandalize a graveyard like this? 53 | -> post_fire 54 | 55 | = post_fire 56 | #fog:worse 57 | { 58 | - fire1 and fire2: 59 | Someone is doing Halloween wrong. It's not until tomorrow, people! 60 | -> bob.chase 61 | - else: 62 | -> explore_map 63 | } -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/totem1.ink: -------------------------------------------------------------------------------- 1 | 2 | 3 | == totem1 4 | #showdialog 5 | { 6 | - bob.chase: -> talk_to 7 | - else: 8 | That's strange, I wonder what this thing is? 9 | -> explore_map 10 | } 11 | 12 | = talk_to 13 | #showdialog 14 | { 15 | - totem2: 16 | {totem2.defeat: Hey now, if you thought the other one was hard, watch out for me! Dare you? | Scared of us? Want to try me? } 17 | - else: 18 | {totem1.wrong: Hahaha back for more?} Do you dare disturb me? 19 | } 20 | + [Sure do] 21 | + [No, sorry, nevermind] -> explore_map 22 | - 23 | 24 | The key to my defeat are these riddles 3: 25 | 26 | Susan and Terence are dead on the ground. They are naked and wet, but there are no cuts or bruises on them. It is stormy tonight. How did they die? 27 | * Struck by lightning -> wrong 28 | * Poisoned -> wrong 29 | * Drowned -> wrong 30 | + They're fish 31 | - 32 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 33 | 34 | It is a small treasure chest found within sands, what lies inside? 35 | * Dubloons -> wrong 36 | + A pearl 37 | * Sand dollar -> wrong 38 | * My uncle’s wallet -> wrong 39 | - 40 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 41 | 42 | You grow it real young. The witch has a big one. It’s a cavernous place, but I’d not be without one. What is it? 43 | * Ears -> wrong 44 | * Garden -> wrong 45 | * Puppy -> wrong 46 | + Nose 47 | - 48 | {{~Great job!|Very good.|Not much of a challenge}|You knew that one.} 49 | 50 | -> defeat 51 | 52 | = defeat 53 | No! How could you! #defeat:totem1 54 | {totem2.defeat: -> bob.last_totem } 55 | 56 | -> explore_map 57 | 58 | = wrong 59 | Go away kid, come back when you're serious about word-play and lateral thinking. 60 | 61 | -> explore_map -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/state_element.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | # ############################################################################ # 13 | # !! VALUE TYPE 14 | # ############################################################################ # 15 | 16 | # This element is only used during JSON parsing and is never duplicated / passed 17 | # around so it doesn't need to be either immutable or have a 'duplicate' method. 18 | 19 | class_name InkStateElement 20 | 21 | # ############################################################################ # 22 | 23 | enum State { 24 | NONE, 25 | OBJECT, 26 | ARRAY, 27 | PROPERTY, 28 | PROPERTY_NAME, 29 | STRING, 30 | } 31 | 32 | # ############################################################################ # 33 | 34 | var type: int = State.NONE # State 35 | var child_count: int = 0 36 | 37 | # ############################################################################ # 38 | 39 | func _init(type: int): 40 | self.type = type 41 | 42 | # ############################################################################ # 43 | # GDScript extra methods 44 | # ############################################################################ # 45 | 46 | func is_class(type) -> bool: 47 | return type == "StateElement" || .is_class(type) 48 | 49 | func get_class() -> String: 50 | return "StateElement" 51 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/structures/ink_execution_configuration.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | tool 8 | extends Reference 9 | 10 | ## Contains all the configuration settings necessary to perform an execution. 11 | class_name InkExecutionConfiguration 12 | 13 | # ############################################################################ # 14 | # Properties 15 | # ############################################################################ # 16 | 17 | var use_threads: bool = false 18 | var user_triggered: bool = false 19 | 20 | 21 | var use_mono: bool = false 22 | var mono_path: String = "" 23 | var inklecate_path: String = "" 24 | 25 | # ############################################################################ # 26 | # Overrides 27 | # ############################################################################ # 28 | 29 | func _init( 30 | configuration: InkConfiguration, 31 | use_threads: bool, 32 | user_triggered: bool 33 | ): 34 | self.use_threads = use_threads 35 | self.user_triggered = user_triggered 36 | 37 | self.use_mono = !_is_running_on_windows() && configuration.use_mono 38 | 39 | self.mono_path = configuration.mono_path 40 | self.inklecate_path = configuration.inklecate_path 41 | 42 | # ############################################################################ # 43 | # Private Methods 44 | # ############################################################################ # 45 | 46 | func _is_running_on_windows(): 47 | var os_name = OS.get_name() 48 | return (os_name == "Windows" || os_name == "UWP") 49 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/common/ink_progress_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/inkgd/editor/panel/common/ink_progress_dialog.gd" type="Script" id=1] 4 | 5 | [node name="InkProgressDialog" type="PopupDialog"] 6 | anchor_left = 0.5 7 | anchor_top = 0.5 8 | anchor_right = 0.5 9 | anchor_bottom = 0.5 10 | margin_left = -250.0 11 | margin_top = -42.5 12 | margin_right = 250.0 13 | margin_bottom = 42.5 14 | rect_min_size = Vector2( 500, 85 ) 15 | script = ExtResource( 1 ) 16 | __meta__ = { 17 | "_edit_use_anchors_": false 18 | } 19 | 20 | [node name="MarginContainer" type="MarginContainer" parent="."] 21 | anchor_right = 1.0 22 | anchor_bottom = 1.0 23 | custom_constants/margin_right = 10 24 | custom_constants/margin_top = 10 25 | custom_constants/margin_left = 10 26 | custom_constants/margin_bottom = 10 27 | __meta__ = { 28 | "_edit_use_anchors_": false 29 | } 30 | 31 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 32 | margin_left = 10.0 33 | margin_top = 10.0 34 | margin_right = 490.0 35 | margin_bottom = 75.0 36 | custom_constants/separation = 5 37 | alignment = 1 38 | __meta__ = { 39 | "_edit_use_anchors_": false 40 | } 41 | 42 | [node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"] 43 | margin_top = 1.0 44 | margin_right = 480.0 45 | margin_bottom = 15.0 46 | text = "Compiling..." 47 | __meta__ = { 48 | "_edit_use_anchors_": false 49 | } 50 | 51 | [node name="ProgressBar" type="ProgressBar" parent="MarginContainer/VBoxContainer"] 52 | margin_top = 25.0 53 | margin_right = 480.0 54 | margin_bottom = 39.0 55 | __meta__ = { 56 | "_edit_use_anchors_": false 57 | } 58 | 59 | [node name="CurrentStepLabel" type="Label" parent="MarginContainer/VBoxContainer"] 60 | margin_top = 49.0 61 | margin_right = 480.0 62 | margin_bottom = 63.0 63 | text = "the_intercept.ink" 64 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/structures/ink_compilation_configuration.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends InkExecutionConfiguration 8 | 9 | ## Contains all the configuration settings necessary to perform a compilation. 10 | class_name InkCompilationConfiguration 11 | 12 | # ############################################################################ # 13 | # Properties 14 | # ############################################################################ # 15 | 16 | ## The path to the story to compile, local to the file system. 17 | var source_file_path: String = "" 18 | 19 | ## The path to the compiled story, local to the file system. 20 | var target_file_path: String = "" 21 | 22 | # ############################################################################ # 23 | # Overrides 24 | # ############################################################################ # 25 | 26 | func _init( 27 | configuration: InkConfiguration, 28 | use_threads: bool, 29 | user_triggered: bool, 30 | source_file_path: String, 31 | target_file_path: String 32 | ).(configuration, use_threads, user_triggered): 33 | self.source_file_path = ProjectSettings.globalize_path(source_file_path) 34 | self.target_file_path = ProjectSettings.globalize_path(target_file_path) 35 | 36 | # ############################################################################ # 37 | # Private Methods 38 | # ############################################################################ # 39 | 40 | func _is_running_on_windows(): 41 | var os_name = OS.get_name() 42 | return (os_name == "Windows" || os_name == "UWP") 43 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/choices/choice.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:unused_class_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | extends InkObject 12 | 13 | class_name InkChoice 14 | 15 | # ############################################################################ # 16 | # Imports 17 | # ############################################################################ # 18 | 19 | var CallStack := load("res://addons/inkgd/runtime/callstack.gd") as GDScript 20 | 21 | # ############################################################################ # 22 | 23 | var text: String 24 | 25 | var path_string_on_choice: String setget set_path_string_on_choice, get_path_string_on_choice 26 | func get_path_string_on_choice() -> String: 27 | return target_path._to_string() 28 | 29 | func set_path_string_on_choice(value: String): 30 | target_path = InkPath().new_with_components_string(value) 31 | 32 | # String? 33 | var source_path = null 34 | 35 | var index: int = 0 36 | 37 | # InkPath? 38 | var target_path = null 39 | 40 | # CallStack.InkThread? 41 | var thread_at_generation = null 42 | 43 | var original_thread_index: int = 0 44 | 45 | var is_invisible_default: bool = false 46 | 47 | # Array? 48 | var tags 49 | 50 | # ############################################################################ # 51 | # GDScript extra methods 52 | # ############################################################################ # 53 | 54 | func is_class(type): 55 | return type == "Choice" || .is_class(type) 56 | 57 | func get_class(): 58 | return "Choice" 59 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/int_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkIntValue 15 | 16 | # ############################################################################ # 17 | 18 | func get_value_type(): 19 | return ValueType.INT 20 | 21 | func get_is_truthy(): 22 | return value != 0 23 | 24 | func _init(): 25 | value = 0 26 | 27 | # The method takes a `StoryErrorMetadata` object as a parameter that 28 | # doesn't exist in upstream. The metadat are used in case an 'exception' 29 | # is raised. For more information, see story.gd. 30 | func cast(new_type, metadata = null): 31 | if new_type == self.value_type: 32 | return self 33 | 34 | if new_type == ValueType.BOOL: 35 | return BoolValue().new_with(false if value == 0 else true) 36 | 37 | if new_type == ValueType.FLOAT: 38 | return FloatValue().new_with(float(value)) 39 | 40 | if new_type == ValueType.STRING: 41 | return StringValue().new_with(str(value)) 42 | 43 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 44 | return null 45 | 46 | # ######################################################################## # 47 | # GDScript extra methods 48 | # ######################################################################## # 49 | 50 | func is_class(type): 51 | return type == "IntValue" || .is_class(type) 52 | 53 | func get_class(): 54 | return "IntValue" 55 | 56 | static func new_with(val): 57 | var value = IntValue().new() 58 | value._init_with(val) 59 | return value 60 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/search_result.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:unused_class_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | # ############################################################################ # 12 | # !! VALUE TYPE 13 | # ############################################################################ # 14 | 15 | # Search results are never duplicated / passed around so they don't need to 16 | # be either immutable or have a 'duplicate' method. 17 | 18 | extends InkBase 19 | 20 | class_name InkSearchResult 21 | 22 | # ############################################################################ # 23 | # Self-reference 24 | # ############################################################################ # 25 | 26 | static func SearchResult() -> GDScript: 27 | return load("res://addons/inkgd/runtime/search_result.gd") as GDScript 28 | 29 | # ############################################################################ # 30 | 31 | var obj = null # InkObject 32 | var approximate = false # bool 33 | 34 | var correct_obj setget , get_correct_obj # InkObject 35 | func get_correct_obj(): 36 | return null if approximate else obj 37 | 38 | var container setget , get_container # Container 39 | func get_container(): 40 | return Utils.as_or_null(obj, "InkContainer") 41 | 42 | # ############################################################################ # 43 | # GDScript extra methods 44 | # ############################################################################ # 45 | 46 | func is_class(type: String) -> bool: 47 | return type == "SearchResult" || .is_class(type) 48 | 49 | func get_class() -> String: 50 | return "SearchResult" 51 | -------------------------------------------------------------------------------- /examples/scenes/common/choice_container.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends MarginContainer 8 | 9 | class_name ChoiceContainer 10 | 11 | # ############################################################################ # 12 | # Imports 13 | # ############################################################################ # 14 | 15 | var ChoiceButton = load("res://examples/scenes/common/button.tscn") 16 | 17 | # ############################################################################ # 18 | # Signal 19 | # ############################################################################ # 20 | 21 | signal choice_selected(index) 22 | 23 | # ############################################################################ # 24 | # Private properties 25 | # ############################################################################ # 26 | 27 | var _buttons = [] 28 | 29 | # ############################################################################ # 30 | # Public Methods 31 | # ############################################################################ # 32 | 33 | func create_choices(choices): 34 | for choice in choices: 35 | var button = ChoiceButton.instance() 36 | button.text = choice.text 37 | button.connect("pressed", self, "_button_pressed", [button]) 38 | 39 | _buttons.append(button) 40 | $ChoiceVBoxContainer.add_child(button) 41 | 42 | # ############################################################################ # 43 | # Private Methods 44 | # ############################################################################ # 45 | 46 | func _button_pressed(button): 47 | var index = _buttons.find(button) 48 | 49 | if index != -1: 50 | emit_signal("choice_selected", index) 51 | -------------------------------------------------------------------------------- /addons/inkgd/ink_player_factory.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2018-2021 Paul Joannon 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # Licensed under the MIT License. 5 | # See LICENSE in the project root for license information. 6 | # ############################################################################ # 7 | 8 | tool 9 | extends Reference 10 | 11 | class_name InkPlayerFactory 12 | 13 | const DO_NOT_USE_MONO_RUNTIME_SETTING = "inkgd/do_not_use_mono_runtime" 14 | 15 | # ############################################################################ # 16 | # Methods 17 | # ############################################################################ # 18 | 19 | static func create(): 20 | if _should_use_mono(): 21 | var InkPlayer = load("res://addons/inkgd/mono/InkPlayer.cs") 22 | if InkPlayer.can_instance(): 23 | return InkPlayer.new() 24 | else: 25 | printerr( 26 | "[inkgd] [ERROR] InkPlayer can't be instantiated. Make sure that a suitable " + 27 | "copy of 'ink-runtime-engine.dll' can be found in project and double check " + 28 | "that the .csproj file contains a item pointing to it. " + 29 | "If everything is configured correctly, you may need to rebuild " + 30 | "the C# solution. Please refer to [TO BE ADDED] for additional help." 31 | ) 32 | print("[inkgd] [INFO] Falling back to the GDScript runtime.") 33 | 34 | # Falling back to GDscript. 35 | return load("res://addons/inkgd/ink_player.gd").new() 36 | 37 | 38 | static func _should_use_mono() -> bool: 39 | if ProjectSettings.has_setting(DO_NOT_USE_MONO_RUNTIME_SETTING): 40 | var do_not_use_mono = ProjectSettings.get_setting(DO_NOT_USE_MONO_RUNTIME_SETTING) 41 | if do_not_use_mono == null: 42 | do_not_use_mono = false 43 | 44 | return _can_run_mono() && !do_not_use_mono 45 | else: 46 | return _can_run_mono() 47 | 48 | static func _can_run_mono() -> bool: 49 | return type_exists("_GodotSharp") 50 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/float_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkFloatValue 15 | 16 | # ############################################################################ # 17 | 18 | func get_value_type(): 19 | return ValueType.FLOAT 20 | 21 | func get_is_truthy(): 22 | return value != 0.0 23 | 24 | func _init(): 25 | value = 0.0 26 | 27 | # The method takes a `StoryErrorMetadata` object as a parameter that 28 | # doesn't exist in upstream. The metadat are used in case an 'exception' 29 | # is raised. For more information, see story.gd. 30 | func cast(new_type, metadata = null): 31 | if new_type == self.value_type: 32 | return self 33 | 34 | if new_type == ValueType.BOOL: 35 | return BoolValue().new_with(false if value == 0 else true) 36 | 37 | if new_type == ValueType.INT: 38 | return IntValue().new_with(int(value)) 39 | 40 | if new_type == ValueType.STRING: 41 | return StringValue().new_with(str(value)) # TODO: Check formating 42 | 43 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 44 | return null 45 | 46 | # ######################################################################## # 47 | # GDScript extra methods 48 | # ######################################################################## # 49 | 50 | func is_class(type): 51 | return type == "FloatValue" || .is_class(type) 52 | 53 | func get_class(): 54 | return "FloatValue" 55 | 56 | static func new_with(val): 57 | var value = FloatValue().new() 58 | value._init_with(val) 59 | return value 60 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/bool_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkBoolValue 15 | 16 | # ############################################################################ # 17 | 18 | func get_value_type() -> int: 19 | return ValueType.BOOL 20 | 21 | func get_is_truthy() -> bool: 22 | return value 23 | 24 | func _init(): 25 | value = false 26 | 27 | # The method takes a `StoryErrorMetadata` object as a parameter that 28 | # doesn't exist in upstream. The metadat are used in case an 'exception' 29 | # is raised. For more information, see story.gd. 30 | func cast(new_type, metadata = null): 31 | if new_type == self.value_type: 32 | return self 33 | 34 | if new_type == ValueType.INT: 35 | return IntValue().new_with(1 if value else 0) 36 | 37 | if new_type == ValueType.FLOAT: 38 | return FloatValue().new_with(1.0 if value else 0.0) 39 | 40 | if new_type == ValueType.STRING: 41 | return StringValue().new_with("true" if value else "false") 42 | 43 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 44 | return null 45 | 46 | func _to_string() -> String: 47 | return "true" if value else "false" 48 | 49 | # ######################################################################## # 50 | # GDScript extra methods 51 | # ######################################################################## # 52 | 53 | func is_class(type): 54 | return type == "BoolValue" || .is_class(type) 55 | 56 | func get_class(): 57 | return "BoolValue" 58 | 59 | static func new_with(val): 60 | var value = BoolValue().new() 61 | value._init_with(val) 62 | return value 63 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/divert_target_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkDivertTargetValue 15 | 16 | # ############################################################################ # 17 | 18 | var target_path setget set_target_path, get_target_path # InkPath 19 | func get_target_path(): 20 | return value 21 | func set_target_path(value): 22 | self.value = value 23 | 24 | func get_value_type(): 25 | return ValueType.DIVERT_TARGET 26 | 27 | func get_is_truthy(): 28 | Utils.throw_exception("Shouldn't be checking the truthiness of a divert target") 29 | return false 30 | 31 | func _init(): 32 | value = null 33 | 34 | # The method takes a `StoryErrorMetadata` object as a parameter that 35 | # doesn't exist in upstream. The metadat are used in case an 'exception' 36 | # is raised. For more information, see story.gd. 37 | func cast(new_type, metadata = null): 38 | if new_type == self.value_type: 39 | return self 40 | 41 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 42 | return null 43 | 44 | func _to_string() -> String: 45 | return "DivertTargetValue(" + self.target_path._to_string() + ")" 46 | 47 | # ######################################################################## # 48 | # GDScript extra methods 49 | # ######################################################################## # 50 | 51 | func is_class(type): 52 | return type == "DivertTargetValue" || .is_class(type) 53 | 54 | func get_class(): 55 | return "DivertTargetValue" 56 | 57 | static func new_with(val): 58 | var value = DivertTargetValue().new() 59 | value._init_with(val) 60 | return value 61 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/extra/string_set.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2015-2021 inkle Ltd. 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # All Rights Reserved 5 | # 6 | # This file is part of inkgd. 7 | # inkgd is licensed under the terms of the MIT license. 8 | # ############################################################################ # 9 | 10 | # Using an dictionary as the backing structure for a not-too-bad, super-simple 11 | # set. The Ink runtime doesn't use C#'s HashSet full potential, so this trick 12 | # should be good enough for the use-case. 13 | 14 | # This simple set is designed to hold Strings only. 15 | 16 | extends Reference 17 | 18 | class_name InkStringSet 19 | 20 | # ############################################################################ # 21 | # Self-reference 22 | # ############################################################################ # 23 | 24 | static func InkStringSet() -> GDScript: 25 | return load("res://addons/inkgd/runtime/extra/string_set.gd") as GDScript 26 | 27 | # ############################################################################ # 28 | 29 | var _dictionary: Dictionary = {} 30 | 31 | # ############################################################################ # 32 | 33 | func clear() -> void: 34 | _dictionary.clear() 35 | 36 | func duplicate() -> InkStringSet: 37 | var set = InkStringSet().new() 38 | set._dictionary = _dictionary.duplicate() 39 | return set 40 | 41 | func enumerate() -> Array: 42 | return _dictionary.keys() 43 | 44 | func empty() -> bool: 45 | return _dictionary.empty() 46 | 47 | func contains(element: String) -> bool: 48 | return _dictionary.has(element) 49 | 50 | func contains_all(elements: Array) -> bool: 51 | return _dictionary.has_all(elements) 52 | 53 | func size() -> int: 54 | return _dictionary.size() 55 | 56 | func to_array() -> Array: 57 | return _dictionary.keys() 58 | 59 | func append(value: String) -> void: 60 | _dictionary[value] = null 61 | 62 | func erase(value: String) -> bool: 63 | return _dictionary.erase(value) 64 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/variable_reference.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkVariableReference 15 | 16 | # ############################################################################ # 17 | 18 | # String 19 | var name = null 20 | 21 | # InkPath 22 | var path_for_count = null 23 | 24 | # Container? 25 | var container_for_count setget , get_container_for_count 26 | func get_container_for_count(): 27 | return self.resolve_path(path_for_count).container 28 | 29 | # String? 30 | var path_string_for_count setget set_path_string_for_count , get_path_string_for_count 31 | func get_path_string_for_count(): 32 | if path_for_count == null: 33 | return null 34 | 35 | return compact_path_string(path_for_count) 36 | 37 | func set_path_string_for_count(value): 38 | if value == null: 39 | path_for_count = null 40 | else: 41 | path_for_count = InkPath().new_with_components_string(value) 42 | 43 | # ############################################################################ # 44 | 45 | func _init(name = null): 46 | if name: 47 | self.name = name 48 | 49 | # ############################################################################ # 50 | 51 | func _to_string() -> String: 52 | if name != null: 53 | return "var(%s)" % name 54 | else: 55 | var path_str = self.path_string_for_count 56 | return "read_count(%s)" % path_str 57 | 58 | # ############################################################################ # 59 | # GDScript extra methods 60 | # ############################################################################ # 61 | 62 | func is_class(type: String) -> bool: 63 | return type == "VariableReference" || .is_class(type) 64 | 65 | func get_class() -> String: 66 | return "VariableReference" 67 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/variable_assignment.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkVariableAssignment 15 | 16 | # ############################################################################ # 17 | # Self-reference 18 | # ############################################################################ # 19 | 20 | static func InkVariableAssignment() -> GDScript: 21 | return load("res://addons/inkgd/runtime/content/variable_assignment.gd") as GDScript 22 | 23 | # ############################################################################ # 24 | 25 | # String 26 | var variable_name = null 27 | var is_new_declaration: bool = false 28 | var is_global: bool = false 29 | 30 | func _init(): 31 | _init_with(null, false) 32 | 33 | # (String?, bool) -> InkVariableAssignment 34 | func _init_with(variable_name, is_new_declaration: bool): 35 | self.variable_name = variable_name 36 | self.is_new_declaration = is_new_declaration 37 | 38 | func _to_string() -> String: 39 | return "VarAssign to %s" % variable_name 40 | 41 | # ############################################################################ # 42 | # GDScript extra methods 43 | # ############################################################################ # 44 | 45 | func is_class(type: String) -> bool: 46 | return type == "VariableAssignment" || .is_class(type) 47 | 48 | func get_class() -> String: 49 | return "VariableAssignment" 50 | 51 | # (String?, bool) -> InkVariableAssignment 52 | static func new_with( 53 | variable_name: String, 54 | is_new_declaration: bool 55 | ) -> InkVariableAssignment: 56 | var variable_assignment = InkVariableAssignment().new() 57 | variable_assignment._init_with(variable_name, is_new_declaration) 58 | return variable_assignment 59 | -------------------------------------------------------------------------------- /addons/inkgd/editor/icons/compile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Artboard 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/variable_pointer_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkVariablePointerValue 15 | 16 | # ############################################################################ # 17 | 18 | var variable_name setget set_variable_name, get_variable_name # InkPath 19 | func get_variable_name(): 20 | return value 21 | func set_variable_name(value): 22 | self.value = value 23 | 24 | func get_value_type(): 25 | return ValueType.VARIABLE_POINTER 26 | 27 | func get_is_truthy(): 28 | Utils.throw_exception("Shouldn't be checking the truthiness of a variable pointer") 29 | return false 30 | 31 | var context_index = 0 # int 32 | 33 | func _init_with_context(variable_name, context_index = -1): 34 | ._init_with(variable_name) 35 | self.context_index = context_index 36 | 37 | func _init(): 38 | value = null 39 | 40 | # The method takes a `StoryErrorMetadata` object as a parameter that 41 | # doesn't exist in upstream. The metadat are used in case an 'exception' 42 | # is raised. For more information, see story.gd. 43 | func cast(new_type, metadata = null): 44 | if new_type == self.value_type: 45 | return self 46 | 47 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 48 | return null 49 | 50 | func _to_string() -> String: 51 | return "VariablePointerValue(" + self.variable_name + ")" 52 | 53 | func copy(): 54 | return VariablePointerValue().new_with_context(self.variable_name, context_index) 55 | 56 | # ######################################################################## # 57 | # GDScript extra methods 58 | # ######################################################################## # 59 | 60 | func is_class(type): 61 | return type == "VariablePointerValue" || .is_class(type) 62 | 63 | func get_class(): 64 | return "VariablePointerValue" 65 | 66 | static func new_with_context(variable_name, context_index = -1): 67 | var value = VariablePointerValue().new() 68 | value._init_with_context(variable_name, context_index) 69 | return value 70 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/common/ink_progress_dialog.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | tool 8 | extends PopupDialog 9 | 10 | # A custom dialog showing a progress bar. 11 | 12 | # Hiding this type to prevent registration of "private" nodes. 13 | # See https://github.com/godotengine/godot-proposals/issues/1047 14 | # class_name InkProgressDialog 15 | 16 | # ############################################################################ # 17 | # Nodes 18 | # ############################################################################ # 19 | 20 | onready var _margin_container = $MarginContainer 21 | onready var _vbox_container = $MarginContainer/VBoxContainer 22 | onready var _title_label = $MarginContainer/VBoxContainer/TitleLabel 23 | onready var _progress_bar = $MarginContainer/VBoxContainer/ProgressBar 24 | onready var _current_step_label = $MarginContainer/VBoxContainer/CurrentStepLabel 25 | 26 | # ############################################################################ # 27 | # Properties 28 | # ############################################################################ # 29 | 30 | ## The title of the progress. 31 | var title: String setget set_title, get_title 32 | func set_title(text: String): 33 | _title_label.text = text 34 | func get_title() -> String: 35 | return _title_label.text 36 | 37 | ## The name of the current step. 38 | var current_step_name: String setget set_current_step_name, get_current_step_name 39 | func set_current_step_name(text: String): 40 | _current_step_label.text = text 41 | func get_current_step_name() -> String: 42 | return _current_step_label.text 43 | 44 | ## The current progress. 45 | var progress: float setget set_progress, get_progress 46 | func set_progress(progress: float): 47 | _progress_bar.value = progress 48 | func get_progress() -> float: 49 | return _progress_bar.value 50 | 51 | func update_layout(scale: float) -> void: 52 | _margin_container.add_constant_override("margin_right", 10 * scale) 53 | _margin_container.add_constant_override("margin_top", 10 * scale) 54 | _margin_container.add_constant_override("margin_left", 10 * scale) 55 | _margin_container.add_constant_override("margin_bottom", 10 * scale) 56 | _vbox_container.add_constant_override("separation", 5 * scale) 57 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/string_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkStringValue 15 | 16 | # ############################################################################ # 17 | 18 | func get_value_type(): 19 | return ValueType.STRING 20 | 21 | func get_is_truthy(): 22 | return value.length() > 0 23 | 24 | var is_newline # bool 25 | var is_inline_whitespace # bool 26 | var is_non_whitespace setget , get_is_non_whitespace # bool 27 | func get_is_non_whitespace(): 28 | return !is_newline && !is_inline_whitespace 29 | 30 | func _init(): 31 | value = "" 32 | self._sanitize_value() 33 | 34 | func _init_with(val): 35 | ._init_with(val) 36 | self._sanitize_value() 37 | 38 | # The method takes a `StoryErrorMetadata` object as a parameter that 39 | # doesn't exist in upstream. The metadat are used in case an 'exception' 40 | # is raised. For more information, see story.gd. 41 | func cast(new_type, metadata = null): 42 | if new_type == self.value_type: 43 | return self 44 | 45 | if new_type == ValueType.INT: 46 | if self.value.is_valid_integer(): 47 | return IntValue().new_with(int(self.value)) 48 | else: 49 | return null 50 | 51 | if new_type == ValueType.FLOAT: 52 | if self.value.is_valid_float(): 53 | return FloatValue().new_with(float(self.value)) 54 | else: 55 | return null 56 | 57 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 58 | return null 59 | 60 | # ######################################################################## # 61 | # GDScript extra methods 62 | # ######################################################################## # 63 | 64 | func is_class(type): 65 | return type == "StringValue" || .is_class(type) 66 | 67 | func get_class(): 68 | return "StringValue" 69 | 70 | func _sanitize_value(): 71 | is_newline = (self.value == "\n") 72 | is_inline_whitespace = true 73 | 74 | for c in self.value: 75 | if c != ' ' && c != "\t": 76 | is_inline_whitespace = false 77 | break 78 | 79 | static func new_with(val): 80 | var value = StringValue().new() 81 | value._init_with(val) 82 | return value 83 | -------------------------------------------------------------------------------- /addons/inkgd/editor/import_plugins/ink_json_import_plugin.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2018-2022 Paul Joannon 3 | # Copyright © 2019-2022 Frédéric Maquin 4 | # Licensed under the MIT License. 5 | # See LICENSE in the project root for license information. 6 | # ############################################################################ # 7 | 8 | extends EditorImportPlugin 9 | 10 | class_name InkJsonImportPlugin 11 | 12 | # ############################################################################ # 13 | # Imports 14 | # ############################################################################ # 15 | 16 | var InkConfiguration = load("res://addons/inkgd/editor/common/ink_configuration.gd") 17 | var InkResource = load("res://addons/inkgd/editor/import_plugins/ink_resource.gd") 18 | 19 | # ############################################################################ # 20 | # Properties 21 | # ############################################################################ # 22 | 23 | var _configuration = InkConfiguration.new() 24 | 25 | # ############################################################################ # 26 | # Overrides 27 | # ############################################################################ # 28 | 29 | func get_importer_name(): 30 | return "inkgd.ink.json"; 31 | 32 | func get_visible_name(): 33 | return "Compiled ink story"; 34 | 35 | func get_recognized_extensions(): 36 | return ["json"]; 37 | 38 | func get_save_extension(): 39 | return "res"; 40 | 41 | func get_resource_type(): 42 | return "Resource"; 43 | 44 | func get_import_options(preset): 45 | return [ 46 | { 47 | "name": "compress", 48 | "default_value": true 49 | } 50 | ] 51 | 52 | func get_option_visibility(option, options): 53 | return true 54 | 55 | func get_preset_count(): 56 | return 0 57 | 58 | func import(source_file, save_path, options, r_platform_variants, r_gen_files): 59 | _configuration.retrieve() 60 | 61 | var raw_json = _get_file_content(source_file) 62 | 63 | var json = parse_json(raw_json) 64 | if !json.has("inkVersion"): 65 | return ERR_FILE_UNRECOGNIZED 66 | 67 | var resource = InkResource.new() 68 | resource.json = raw_json 69 | 70 | var flags = ResourceSaver.FLAG_COMPRESS if options["compress"] else 0 71 | return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], resource, flags) 72 | 73 | # ############################################################################ # 74 | # Private Helpers 75 | # ############################################################################ # 76 | 77 | func _get_file_content(source_file): 78 | var file = File.new() 79 | var err = file.open(source_file, File.READ) 80 | if err != OK: 81 | return err 82 | 83 | var text_content = file.get_as_text() 84 | 85 | file.close() 86 | return text_content 87 | -------------------------------------------------------------------------------- /addons/inkgd/editor/icons/ink_player.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Artboard 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/ink/not_a_halloween_game/includes/bob.ink: -------------------------------------------------------------------------------- 1 | 2 | 3 | == bob 4 | {last_totem: -> bad_news} 5 | 6 | hello #showdialog 7 | 8 | -> explore_map 9 | 10 | = chase 11 | #showdialog 12 | #camera:bob 13 | 14 | Bob: Help ME! #bob:run_from_witch 15 | Witch: I'll get you yet! #witch:enter_attacking_bob 16 | Bob: Please, no! #witch:throw_fireball 17 | Witch: hahahahaha #witch:back_to_hotel 18 | (watch the cutscene, and put bob out) #hidedialog #camera:player 19 | + [Bob] #showdialog #bob:extinguish 20 | #camera:bob 21 | Bob: Oh my goodness. Thank you for saving me! 22 | What was all that about? Is that the cranky old lady who yells a lot at town halls? 23 | Bob: Yeah... she's my landlord. It's been awful since I moved here. 24 | Bob: Loretta's a witch. I'm not just being rude, mind you. She's actually practicing some horrible ritual. 25 | Bob: So, Loretta apparently has quite a beef with the town. 26 | Bob: It's taken her a long time to do it, but she has these totems all over town. 27 | Bob: I've found this one, but those fireballs did a number on me. #bob:show_totem 28 | Bob: Please, I need you to find and destroy the others... or the fish... #bob:destroy_totem 29 | Wow, guess I'll get totem-destroying! #bob:rest #camera:player 30 | -> explore_map 31 | 32 | = last_totem 33 | #showdialog 34 | #bob:to_totem 35 | Bob: Incredible! You've only got one more totem to defeat! 36 | Bob: If only you didn't have to destroy them all, but once the spell gets started, it's gotta be stopped completely. 37 | Bob: But... So... there's a coice you need to make. 38 | Bob: I'm the last totem. There was a clause in my rental agreement that allowed her to curse me as one of her totems. 39 | Bob: Always read the fine print, man. 40 | Bob: Anyway, you gotta kill me to break the spell that's still brewing. 41 | Bob: I'll be down by the southern beach. #bob:to_beach 42 | 43 | -> explore_map 44 | 45 | = bad_news 46 | #showdialog 47 | 48 | Bob: Did you make your choice? 49 | + [Yes] 50 | Bob: So, you ready to kill me? #witch:enter_laughing 51 | + + [Yes] I'm ready... -> kill 52 | + + No[], I'm going to save you yet! -> save_again 53 | - - 54 | + [No] 55 | - -> explore_map 56 | 57 | = kill 58 | You hesitate, but if it's the only way... #witch:to_beach 59 | Witch: What's happening? #fog:clear 60 | Bob: Take care of my cat for me... and always check your rental agreement... #bob:stab 61 | #fadetoblack 62 | Witch: The spell! It's failing! Noooo 63 | Witch: It's going to take forever scowling ebay for more totems! I can't afford that 'buy it now' price! 64 | Sad End. 65 | 66 | -> credits 67 | 68 | = save_again 69 | Bob: What? But the spell, the summoning!? The fish people are coming! 70 | (fishmen cutscene) #hidedialog #lightning #fishmen:enter_from_sea #contshowdialog:5 71 | Witch: Yes! Come out of the sea! Take over their homes! Destroy!! 72 | Bob: What... what have you done? 73 | Witch: What are they doing? What have you done? 74 | You notice that the fishmen are just milling about. Maybe we should talk to them. 75 | 76 | -> explore_map 77 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/debug_metadata.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:unused_class_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | extends InkBase 12 | 13 | class_name InkDebugMetadata 14 | 15 | # ############################################################################ # 16 | 17 | var start_line_number: int = 0 18 | var end_line_number: int = 0 19 | var start_character_number: int = 0 20 | var end_character_number: int = 0 21 | # String? 22 | var file_name = null 23 | # String? 24 | var source_name = null 25 | 26 | # ############################################################################ # 27 | 28 | func merge(dm: InkDebugMetadata) -> InkDebugMetadata: 29 | var new_debug_metadata = DebugMetadata().new() 30 | 31 | new_debug_metadata.file_name = self.file_name 32 | new_debug_metadata.source_name = self.source_name 33 | 34 | if self.start_line_number < dm.start_line_number: 35 | new_debug_metadata.start_line_number = self.start_line_number 36 | new_debug_metadata.start_character_number = self.start_character_number 37 | elif self.start_line_number > dm.start_line_number: 38 | new_debug_metadata.start_line_number = dm.start_line_number 39 | new_debug_metadata.start_character_number = dm.start_character_number 40 | else: 41 | var min_scn = min(self.start_character_number, dm.start_character_number) 42 | new_debug_metadata.start_line_number = self.start_line_number 43 | new_debug_metadata.start_character_number = min_scn 44 | 45 | if self.end_line_number > dm.end_line_number: 46 | new_debug_metadata.end_line_number = self.end_line_number 47 | new_debug_metadata.end_character_number = self.end_character_number 48 | elif self.end_line_number < dm.end_line_number: 49 | new_debug_metadata.end_line_number = dm.end_line_number 50 | new_debug_metadata.end_character_number = dm.end_character_number 51 | else: 52 | var max_scn = min(self.end_character_number, dm.end_character_number) 53 | new_debug_metadata.end_line_number = self.end_line_number 54 | new_debug_metadata.end_character_number = max_scn 55 | 56 | return new_debug_metadata 57 | 58 | # () -> String 59 | func _to_string() -> String: 60 | if file_name != null: 61 | return str("line ", start_line_number, " of ", file_name) 62 | else: 63 | return str("line ", start_line_number) 64 | 65 | # ############################################################################ # 66 | # GDScript extra methods 67 | # ############################################################################ # 68 | 69 | func is_class(type: String) -> bool: 70 | return type == "DebugMetadata" || .is_class(type) 71 | 72 | func get_class() -> String: 73 | return "DebugMetadata" 74 | 75 | static func DebugMetadata(): 76 | return load("res://addons/inkgd/runtime/debug_metadata.gd") 77 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/list_value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkValue 13 | 14 | class_name InkListValue 15 | 16 | # ############################################################################ # 17 | 18 | func get_value_type(): 19 | return ValueType.LIST 20 | 21 | func get_is_truthy(): 22 | return value.size() > 0 23 | 24 | # The method takes a `StoryErrorMetadata` object as a parameter that 25 | # doesn't exist in upstream. The metadat are used in case an 'exception' 26 | # is raised. For more information, see story.gd. 27 | func cast(new_type, metadata = null): 28 | if new_type == ValueType.INT: 29 | var max_item = value.max_item 30 | if max_item.key.is_null: 31 | return IntValue().new_with(0) 32 | else: 33 | return IntValue().new_with(max_item.value) 34 | 35 | elif new_type == ValueType.FLOAT: 36 | var max_item = value.max_item 37 | if max_item.key.is_null: 38 | return FloatValue().new_with(0.0) 39 | else: 40 | return FloatValue().new_with(float(max_item.value)) 41 | 42 | elif new_type == ValueType.STRING: 43 | var max_item = value.max_item 44 | if max_item.key.is_null: 45 | return StringValue().new_with("") 46 | else: 47 | return StringValue().new_with(max_item.key._to_string()) 48 | 49 | if new_type == self.value_type: 50 | return self 51 | 52 | Utils.throw_story_exception(bad_cast_exception_message(new_type), false, metadata) 53 | return null 54 | 55 | func _init(): 56 | value = InkList.new() 57 | 58 | func _init_with_list(list): 59 | value = InkList.new_with_ink_list(list) 60 | 61 | func _init_with_single_item(single_item, single_value): 62 | value = InkList.new_with_single_item(single_item, single_value) 63 | 64 | # (InkObject, InkObject) -> void 65 | static func retain_list_origins_for_assignment(old_value, new_value): 66 | var Utils = load("res://addons/inkgd/runtime/extra/utils.gd") 67 | 68 | var old_list = Utils.as_or_null(old_value, "ListValue") 69 | var new_list = Utils.as_or_null(new_value, "ListValue") 70 | 71 | if old_list && new_list && new_list.value.size() == 0: 72 | new_list.value.set_initial_origin_names(old_list.value.origin_names) 73 | 74 | # ############################################################################ # 75 | # GDScript extra methods 76 | # ############################################################################ # 77 | 78 | func is_class(type): 79 | return type == "ListValue" || .is_class(type) 80 | 81 | func get_class(): 82 | return "ListValue" 83 | 84 | static func new_with(list): 85 | var value = ListValue().new() 86 | value._init_with_list(list) 87 | return value 88 | 89 | static func new_with_single_item(single_item, single_value): 90 | var value = ListValue().new() 91 | value._init_with_single_item(single_item, single_value) 92 | return value 93 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/common/ink_rich_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/inkgd/editor/panel/common/ink_rich_dialog.gd" type="Script" id=1] 4 | 5 | [node name="WindowDialog" type="WindowDialog"] 6 | anchor_left = 0.5 7 | anchor_top = 0.5 8 | anchor_right = 0.5 9 | anchor_bottom = 0.5 10 | margin_left = -250.0 11 | margin_top = -150.0 12 | margin_right = 250.0 13 | margin_bottom = 150.0 14 | rect_min_size = Vector2( 500, 300 ) 15 | window_title = "Error" 16 | resizable = true 17 | script = ExtResource( 1 ) 18 | __meta__ = { 19 | "_edit_use_anchors_": false 20 | } 21 | 22 | [node name="MarginContainer" type="MarginContainer" parent="."] 23 | anchor_right = 1.0 24 | anchor_bottom = 1.0 25 | size_flags_horizontal = 3 26 | size_flags_vertical = 3 27 | custom_constants/margin_right = 10 28 | custom_constants/margin_top = 10 29 | custom_constants/margin_left = 10 30 | custom_constants/margin_bottom = 10 31 | __meta__ = { 32 | "_edit_use_anchors_": false 33 | } 34 | 35 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 36 | margin_left = 10.0 37 | margin_top = 10.0 38 | margin_right = 490.0 39 | margin_bottom = 290.0 40 | custom_constants/separation = 10 41 | 42 | [node name="MessageLabel" type="Label" parent="MarginContainer/VBoxContainer"] 43 | margin_right = 480.0 44 | margin_bottom = 31.0 45 | text = "Something went wrong while testing inklecate's setup. Please see the output below." 46 | autowrap = true 47 | 48 | [node name="OutputPanel" type="Panel" parent="MarginContainer/VBoxContainer"] 49 | margin_top = 41.0 50 | margin_right = 480.0 51 | margin_bottom = 250.0 52 | size_flags_horizontal = 3 53 | size_flags_vertical = 3 54 | 55 | [node name="OutputScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/OutputPanel"] 56 | anchor_right = 1.0 57 | anchor_bottom = 1.0 58 | __meta__ = { 59 | "_edit_use_anchors_": false 60 | } 61 | 62 | [node name="OutputMarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/OutputPanel/OutputScrollContainer"] 63 | margin_right = 480.0 64 | margin_bottom = 136.0 65 | size_flags_horizontal = 3 66 | custom_constants/margin_right = 10 67 | custom_constants/margin_top = 10 68 | custom_constants/margin_left = 10 69 | custom_constants/margin_bottom = 10 70 | __meta__ = { 71 | "_edit_use_anchors_": false 72 | } 73 | 74 | [node name="OutputLabel" type="Label" parent="MarginContainer/VBoxContainer/OutputPanel/OutputScrollContainer/OutputMarginContainer"] 75 | margin_left = 10.0 76 | margin_top = 10.0 77 | margin_right = 470.0 78 | margin_bottom = 126.0 79 | size_flags_vertical = 0 80 | text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 81 | autowrap = true 82 | 83 | [node name="AcceptButton" type="Button" parent="MarginContainer/VBoxContainer"] 84 | margin_left = 224.0 85 | margin_top = 260.0 86 | margin_right = 255.0 87 | margin_bottom = 280.0 88 | size_flags_horizontal = 4 89 | text = "OK" 90 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/ink_editor_interface.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Reference 8 | 9 | class_name InkEditorInterface 10 | 11 | # ############################################################################ # 12 | # Signals 13 | # ############################################################################ # 14 | 15 | ## Emitted when 'Ink' resources (i. e. files with the '.ink' extension) were 16 | ## reimported by Godot. 17 | signal ink_ressources_reimported(resources) 18 | 19 | # ############################################################################ # 20 | # Properties 21 | # ############################################################################ # 22 | 23 | ## The pixel display scale of the editor. 24 | var scale: float = 1.0 25 | 26 | var editor_interface: EditorInterface 27 | var editor_settings: EditorSettings 28 | var editor_filesystem: EditorFileSystem 29 | 30 | ## `true` if the editor is running on Windows, `false` otherwise. 31 | var is_running_on_windows: bool setget , get_is_running_on_windows 32 | func get_is_running_on_windows() -> bool: 33 | var os_name = OS.get_name() 34 | return (os_name == "Windows" || os_name == "UWP") 35 | 36 | 37 | # ############################################################################ # 38 | # Overrides 39 | # ############################################################################ # 40 | 41 | func _init(editor_interface: EditorInterface): 42 | self.editor_interface = editor_interface 43 | self.editor_settings = editor_interface.get_editor_settings() 44 | self.editor_filesystem = editor_interface.get_resource_filesystem() 45 | 46 | scale = editor_interface.get_editor_scale() 47 | 48 | self.editor_filesystem.connect("resources_reimported", self, "_resources_reimported") 49 | 50 | # ############################################################################ # 51 | # Methods 52 | # ############################################################################ # 53 | 54 | ## Tell Godot to scan for updated resources. 55 | func scan_file_system(): 56 | self.editor_filesystem.scan() 57 | 58 | ## Tell Godot to scan the given resource. 59 | func update_file(path: String): 60 | self.editor_filesystem.update_file(path) 61 | 62 | ## Returns a custom header color based on the editor's base color. 63 | ## 64 | ## If the base color is not found, return 'Color.transparent'. 65 | func get_custom_header_color() -> Color: 66 | var color = self.editor_settings.get_setting("interface/theme/base_color") 67 | if color != null: 68 | return Color.from_hsv(color.h * 0.99, color.s * 0.6, color.v * 1.1) 69 | else: 70 | return Color.transparent 71 | 72 | # ############################################################################ # 73 | # Signal Receivers 74 | # ############################################################################ # 75 | 76 | func _resources_reimported(resources): 77 | var ink_resources := PoolStringArray() 78 | 79 | for resource in resources: 80 | if resource.get_extension() == "ink": 81 | ink_resources.append(resource) 82 | 83 | emit_signal("ink_ressources_reimported", ink_resources) 84 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/lists/list_definitions_origin.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkListDefinitionsOrigin 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | var InkTryGetResult = preload("res://addons/inkgd/runtime/extra/try_get_result.gd") 21 | var InkListItem = preload("res://addons/inkgd/runtime/lists/structs/ink_list_item.gd") 22 | 23 | var InkListValue = load("res://addons/inkgd/runtime/values/list_value.gd") 24 | 25 | # ############################################################################ # 26 | 27 | # Array 28 | var lists: Array setget , get_lists 29 | func get_lists() -> Array: 30 | var list_of_lists = [] 31 | for named_list_key in _lists: 32 | list_of_lists.append(_lists[named_list_key]) 33 | 34 | return list_of_lists 35 | 36 | # ############################################################################ # 37 | 38 | # (Array) -> InkListDefinitionOrigin 39 | func _init(lists: Array): 40 | _lists = {} # Dictionary 41 | _all_unambiguous_list_value_cache = {} # Dictionary() 42 | 43 | for list in lists: 44 | _lists[list.name] = list 45 | 46 | for item_with_value_key in list.items: 47 | var item = InkListItem.from_serialized_key(item_with_value_key) 48 | var val = list.items[item_with_value_key] 49 | var list_value = InkListValue.new_with_single_item(item, val) 50 | 51 | _all_unambiguous_list_value_cache[item.item_name] = list_value 52 | _all_unambiguous_list_value_cache[item.full_name] = list_value 53 | 54 | 55 | # ############################################################################ # 56 | 57 | # (String) -> { result: String, exists: bool } 58 | func try_list_get_definition(name: String) -> InkTryGetResult: 59 | if name == null: 60 | return InkTryGetResult.new(false, null) 61 | 62 | var definition = _lists.get(name) 63 | if !definition: 64 | return InkTryGetResult.new(false, null) 65 | 66 | return InkTryGetResult.new(true, definition) 67 | 68 | func find_single_item_list_with_name(name: String) -> InkListValue: 69 | if _all_unambiguous_list_value_cache.has(name): 70 | return _all_unambiguous_list_value_cache[name] 71 | 72 | return null 73 | 74 | # ############################################################################ # 75 | 76 | var _lists: Dictionary # Dictionary 77 | var _all_unambiguous_list_value_cache: Dictionary # Dictionary 78 | 79 | # ############################################################################ # 80 | # GDScript extra methods 81 | # ############################################################################ # 82 | 83 | func is_class(type: String) -> bool: 84 | return type == "InkListDefinitionsOrigin" || .is_class(type) 85 | 86 | func get_class() -> String: 87 | return "InkListDefinitionsOrigin" 88 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/ink_csharp_validator.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends Reference 8 | 9 | # A crude validator catching the most common mistakes. 10 | 11 | class_name InkCSharpValidator 12 | 13 | const INK_ENGINE_RUNTIME = "ink-engine-runtime.dll" 14 | 15 | # ############################################################################ # 16 | # Methods 17 | # ############################################################################ # 18 | 19 | func validate_csharp_project_files(project_name) -> bool: 20 | var ink_engine_runtime := get_runtime_path() 21 | 22 | if ink_engine_runtime.empty(): 23 | print( 24 | "[inkgd] [INFO] 'ink-engine-runtime.dll' seems to be missing " + 25 | "from the project. If you encounter errors while building the " + 26 | "solution, please refer to [TO BE ADDED] for help." 27 | ) 28 | return false 29 | 30 | return _validate_csproj(project_name, ink_engine_runtime) 31 | 32 | func get_runtime_path() -> String: 33 | return _scan_directory("res://") 34 | 35 | func _validate_csproj(project_name: String, runtime_path: String) -> bool: 36 | var csproj_path = "res://%s.csproj" % project_name 37 | 38 | var file = File.new() 39 | if !file.file_exists(csproj_path): 40 | printerr( 41 | ("[inkgd] [ERROR] The C# project (%s.csproj) doesn't exist. " % project_name) + 42 | "You can create a new C# project through " + 43 | "Project > Tools > C# > Create C# Solution. Alternatively, you can also set " + 44 | "Project Settings > General > Inkgd > Do Not Use Mono Runtime to 'Yes' " + 45 | "if you do not wish to use the C# version of Ink. " 46 | ) 47 | return false 48 | 49 | var error = file.open(csproj_path, File.READ) 50 | if error != OK: 51 | printerr( 52 | "[inkgd] [ERROR] The C# project (%s.csproj) exists but it could not be opened." + 53 | "(Code %d)" % [project_name, error] 54 | ) 55 | return false 56 | 57 | var content = file.get_as_text() 58 | file.close() 59 | 60 | if content.find(runtime_path.replace("res://", "")) == -1: 61 | print( 62 | "[inkgd] [INFO] '%s.csproj' seems to be missing a " % project_name + 63 | " item matching '%s'. If you encounter " % runtime_path + 64 | "further errors please refer to [TO BE ADDED] for help." 65 | ) 66 | return false 67 | 68 | print("[inkgd] [INFO] The C# Project seems to be configured correctly.") 69 | return true 70 | 71 | func _scan_directory(path) -> String: 72 | var directory := Directory.new() 73 | if directory.open(path) != OK: 74 | printerr( 75 | "[inkgd] [ERROR] Could not open '%s', " % path + 76 | "can't look for ink-engine-runtime.dll." 77 | ) 78 | return "" 79 | 80 | if directory.list_dir_begin(true, true) != OK: 81 | printerr( 82 | "[inkgd] [ERROR] Could not list contents of '%s', " % path + 83 | "can't look for ink-engine-runtime.dll." 84 | ) 85 | return "" 86 | 87 | var file_name := directory.get_next() 88 | while file_name != "": 89 | if directory.current_is_dir(): 90 | var ink_runtime = _scan_directory( 91 | "%s/%s" % [directory.get_current_dir(), file_name] 92 | ) 93 | 94 | if !ink_runtime.empty(): 95 | return ink_runtime 96 | else: 97 | if file_name == INK_ENGINE_RUNTIME: 98 | return "%s/%s" % [directory.get_current_dir(), file_name] 99 | 100 | file_name = directory.get_next() 101 | 102 | return "" 103 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/choices/choice_point.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkChoicePoint 15 | 16 | # ############################################################################ # 17 | 18 | # () -> InkPath 19 | # (InkPath) -> void 20 | var path_on_choice: InkPath setget set_path_on_choice, get_path_on_choice 21 | func get_path_on_choice() -> InkPath: 22 | if self._path_on_choice != null && self._path_on_choice.is_relative: 23 | var choice_target_obj = self.choice_target 24 | if choice_target_obj: 25 | self._path_on_choice = choice_target_obj.path 26 | 27 | return _path_on_choice 28 | func set_path_on_choice(value: InkPath): 29 | _path_on_choice = value 30 | 31 | # InkPath? 32 | var _path_on_choice = null 33 | 34 | # ############################################################################ # 35 | 36 | var choice_target: InkContainer setget , get_choice_target 37 | func get_choice_target() -> InkContainer: 38 | var cont = resolve_path(self._path_on_choice).container 39 | return cont 40 | 41 | # ############################################################################ # 42 | 43 | var path_string_on_choice: String setget \ 44 | set_path_string_on_choice, \ 45 | get_path_string_on_choice 46 | func get_path_string_on_choice() -> String: 47 | return compact_path_string(self.path_on_choice) 48 | func set_path_string_on_choice(value: String): 49 | self.path_on_choice = InkPath().new_with_components_string(value) 50 | 51 | # ############################################################################ # 52 | 53 | var has_condition: bool 54 | var has_start_content: bool 55 | var has_choice_only_content: bool 56 | var once_only: bool 57 | var is_invisible_default: bool 58 | 59 | # ############################################################################ # 60 | 61 | var flags: int setget set_flags, get_flags 62 | func get_flags() -> int: 63 | var flags: int = 0 64 | 65 | if has_condition: 66 | flags |= 1 67 | if has_start_content: 68 | flags |= 2 69 | if has_choice_only_content: 70 | flags |= 4 71 | if is_invisible_default: 72 | flags |= 8 73 | if once_only: 74 | flags |= 16 75 | 76 | return flags 77 | func set_flags(value: int): 78 | has_condition = (value & 1) > 0 79 | has_start_content = (value & 2) > 0 80 | has_choice_only_content = (value & 4) > 0 81 | is_invisible_default = (value & 8) > 0 82 | once_only = (value & 16) > 0 83 | 84 | # ############################################################################ # 85 | 86 | func _init(once_only: bool = true): 87 | self.once_only = once_only 88 | 89 | func _to_string() -> String: 90 | var target_line_num = debug_line_number_of_path(self.path_on_choice) 91 | var target_string = self.path_on_choice._to_string() 92 | 93 | if target_line_num != null: 94 | target_string = " line %d(%s)" % [target_line_num, target_string] 95 | 96 | return "Choice: -> %s" % target_string 97 | 98 | # ############################################################################ # 99 | # GDScript extra methods 100 | # ############################################################################ # 101 | 102 | func is_class(type: String) -> bool: 103 | return type == "ChoicePoint" || .is_class(type) 104 | 105 | func get_class() -> String: 106 | return "ChoicePoint" 107 | -------------------------------------------------------------------------------- /addons/inkgd/editor/templates/ink_template.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:return_value_discarded 2 | 3 | extends %BASE% 4 | 5 | # ############################################################################ # 6 | # Imports 7 | # ############################################################################ # 8 | 9 | var InkPlayer = load("res://addons/inkgd/ink_player.gd") 10 | 11 | # ############################################################################ # 12 | # Public Nodes 13 | # ############################################################################ # 14 | 15 | # Alternatively, it could also be retrieved from the tree. 16 | # onready var _ink_player = $InkPlayer 17 | onready var _ink_player = InkPlayer.new() 18 | 19 | # ############################################################################ # 20 | # Lifecycle 21 | # ############################################################################ # 22 | 23 | func _ready(): 24 | %TS%# Adds the player to the tree. 25 | %TS%add_child(_ink_player) 26 | 27 | %TS%# Replace the example path with the path to your story. 28 | %TS%# Remove this line if you set 'ink_file' in the inspector. 29 | %TS%_ink_player.ink_file = load("res://path/to/file.ink.json") 30 | 31 | %TS%# It's recommended to load the story in the background. On platforms that 32 | %TS%# don't support threads, the value of this variable is ignored. 33 | %TS%_ink_player.loads_in_background = true 34 | 35 | %TS%_ink_player.connect("loaded", self, "_story_loaded") 36 | 37 | %TS%# Creates the story. 'loaded' will be emitted once Ink is ready 38 | %TS%# continue the story. 39 | %TS%_ink_player.create_story() 40 | 41 | 42 | # ############################################################################ # 43 | # Signal Receivers 44 | # ############################################################################ # 45 | 46 | func _story_loaded(successfully: bool): 47 | %TS%if !successfully: 48 | %TS%%TS%return 49 | 50 | %TS%# _observe_variables() 51 | %TS%# _bind_externals() 52 | 53 | %TS%_continue_story() 54 | 55 | 56 | # ############################################################################ # 57 | # Private Methods 58 | # ############################################################################ # 59 | 60 | func _continue_story(): 61 | %TS%while _ink_player.can_continue: 62 | %TS%%TS%var text = _ink_player.continue_story() 63 | %TS%%TS%# This text is a line of text from the ink story. 64 | %TS%%TS%# Set the text of a Label to this value to display it in your game. 65 | %TS%%TS%print(text) 66 | %TS%if _ink_player.has_choices: 67 | %TS%%TS%# 'current_choices' contains a list of the choices, as strings. 68 | %TS%%TS%for choice in _ink_player.current_choices: 69 | %TS%%TS%%TS%print(choice.text) 70 | %TS%%TS%%TS%print(choice.tags) 71 | %TS%%TS%# '_select_choice' is a function that will take the index of 72 | %TS%%TS%# your selection and continue the story. 73 | %TS%%TS%_select_choice(0) 74 | %TS%else: 75 | %TS%%TS%# This code runs when the story reaches it's end. 76 | %TS%%TS%print("The End") 77 | 78 | 79 | func _select_choice(index): 80 | %TS%_ink_player.choose_choice_index(index) 81 | %TS%_continue_story() 82 | 83 | 84 | # Uncomment to bind an external function. 85 | # 86 | # func _bind_externals(): 87 | # %TS%_ink_player.bind_external_function("", self, "_external_function") 88 | # 89 | # 90 | # func _external_function(arg1, arg2): 91 | # %TS%pass 92 | 93 | 94 | # Uncomment to observe the variables from your ink story. 95 | # You can observe multiple variables by putting adding them in the array. 96 | # func _observe_variables(): 97 | # %TS%_ink_player.observe_variables(["var1", "var2"], self, "_variable_changed") 98 | # 99 | # 100 | # func _variable_changed(variable_name, new_value): 101 | # %TS%print("Variable '%s' changed to: %s" %[variable_name, new_value]) 102 | -------------------------------------------------------------------------------- /addons/inkgd/editor/templates/ink_template_signals.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:return_value_discarded 2 | 3 | extends %BASE% 4 | 5 | # ############################################################################ # 6 | # Imports 7 | # ############################################################################ # 8 | 9 | var InkPlayer = load("res://addons/inkgd/ink_player.gd") 10 | 11 | 12 | # ############################################################################ # 13 | # Public Nodes 14 | # ############################################################################ # 15 | 16 | # Alternatively, it could also be retrieved from the tree. 17 | # onready var _ink_player = $InkPlayer 18 | onready var _ink_player = InkPlayer.new() 19 | 20 | # ############################################################################ # 21 | # Lifecycle 22 | # ############################################################################ # 23 | 24 | func _ready(): 25 | %TS%# Adds the player to the tree. 26 | %TS%add_child(_ink_player) 27 | 28 | %TS%# Replace the example path with the path to your story. 29 | %TS%# Remove this line if you set 'ink_file' in the inspector. 30 | %TS%_ink_player.ink_file = load("res://path/to/file.ink.json") 31 | 32 | %TS%# It's recommended to load the story in the background. On platforms that 33 | %TS%# don't support threads, the value of this variable is ignored. 34 | %TS%_ink_player.loads_in_background = true 35 | 36 | %TS%_ink_player.connect("loaded", self, "_story_loaded") 37 | %TS%_ink_player.connect("continued", self, "_continued") 38 | %TS%_ink_player.connect("prompt_choices", self, "_prompt_choices") 39 | %TS%_ink_player.connect("ended", self, "_ended") 40 | 41 | %TS%# Creates the story. 'loaded' will be emitted once Ink is ready 42 | %TS%# continue the story. 43 | %TS%_ink_player.create_story() 44 | 45 | # ############################################################################ # 46 | # Signal Receivers 47 | # ############################################################################ # 48 | 49 | func _story_loaded(successfully: bool): 50 | %TS%if !successfully: 51 | %TS%%TS%return 52 | 53 | %TS%# _observe_variables() 54 | %TS%# _bind_externals() 55 | 56 | %TS%# Here, the story is started immediately, but it could be started 57 | %TS%# at a later time. 58 | %TS%_ink_player.continue_story() 59 | 60 | 61 | func _continued(text, tags): 62 | %TS%print(text) 63 | %TS%# Here you could yield for an hypothetical signal, before continuing. 64 | %TS%# yield(self, "event") 65 | %TS%_ink_player.continue_story() 66 | 67 | 68 | # ############################################################################ # 69 | # Private Methods 70 | # ############################################################################ # 71 | 72 | func _prompt_choices(choices): 73 | %TS%if !choices.empty(): 74 | %TS%%TS%print(choices) 75 | 76 | %TS%%TS%# In a real world scenario, _select_choice' could be 77 | %TS%%TS%# connected to a signal, like 'Button.pressed'. 78 | %TS%%TS%_select_choice(0) 79 | 80 | 81 | func _ended(): 82 | %TS%print("The End") 83 | 84 | 85 | func _select_choice(index): 86 | %TS%_ink_player.choose_choice_index(index) 87 | %TS%_ink_player.continue_story() 88 | 89 | 90 | # Uncomment to bind an external function. 91 | # 92 | # func _bind_externals(): 93 | # %TS%_ink_player.bind_external_function("", self, "_external_function") 94 | # 95 | # 96 | # func _external_function(arg1, arg2): 97 | # %TS%pass 98 | 99 | 100 | # Uncomment to observe the variables from your ink story. 101 | # You can observe multiple variables by putting adding them in the array. 102 | # func _observe_variables(): 103 | # %TS%_ink_player.observe_variables(["var1", "var2"], self, "_variable_changed") 104 | # 105 | # 106 | # func _variable_changed(variable_name, new_value): 107 | # %TS%print("Variable '%s' changed to: %s" %[variable_name, new_value]) 108 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/lists/list_definition.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkListDefinition 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | var InkTryGetResult = preload("res://addons/inkgd/runtime/extra/try_get_result.gd") 21 | var InkListItem = preload("res://addons/inkgd/runtime/lists/structs/ink_list_item.gd") 22 | 23 | # ############################################################################ # 24 | 25 | var name: String setget , get_name 26 | func get_name() -> String: 27 | return _name 28 | 29 | # Dictionary => Dictionary 30 | # Note: 'InkListItem' should actually be serialized into a String, because it 31 | # needs to be a value type. 32 | var items: Dictionary setget , get_items 33 | func get_items() -> Dictionary: 34 | if _items == null: 35 | _items = {} 36 | for item_name_and_value_key in _item_name_to_values: 37 | var item = InkListItem.new_with_origin_name(self.name, item_name_and_value_key) 38 | _items[item.serialized()] = _item_name_to_values[item_name_and_value_key] 39 | 40 | return _items 41 | var _items 42 | 43 | # ############################################################################ # 44 | 45 | func value_for_item(item: InkListItem) -> int: 46 | if (_item_name_to_values.has(item.item_name)): 47 | var intVal = _item_name_to_values[item.item_name] 48 | return intVal 49 | else: 50 | return 0 51 | 52 | func contains_item(item: InkListItem) -> bool: 53 | if item.origin_name != self.name: 54 | return false 55 | 56 | return _item_name_to_values.has(item.item_name) 57 | 58 | func contains_item_with_name(item_name: String) -> bool: 59 | return _item_name_to_values.has(item_name) 60 | 61 | # (int) -> { result: InkListItem, exists: bool } 62 | func try_get_item_with_value(val: int) -> InkTryGetResult: 63 | for named_item_key in _item_name_to_values: 64 | if (_item_name_to_values[named_item_key] == val): 65 | return InkTryGetResult.new( 66 | true, 67 | InkListItem.new_with_origin_name(self.name, named_item_key) 68 | ) 69 | 70 | return InkTryGetResult.new(false, InkListItem.null()) 71 | 72 | # (InkListItem) -> { result: InkListItem, exists: bool } 73 | func try_get_value_for_item(item: InkListItem) -> InkTryGetResult: 74 | if !item.item_name: 75 | return InkTryGetResult.new(false, 0) 76 | 77 | var value = _item_name_to_values.get(item.item_name) 78 | 79 | if (!value): 80 | InkTryGetResult.new(false, 0) 81 | 82 | return InkTryGetResult.new(true, value) 83 | 84 | # (String name, Dictionary) -> InkListDefinition 85 | func _init(name: String, items: Dictionary): 86 | _name = name 87 | _item_name_to_values = items 88 | 89 | var _name: String 90 | var _item_name_to_values: Dictionary # Dictionary 91 | 92 | # ############################################################################ # 93 | # GDScript extra methods 94 | # ############################################################################ # 95 | 96 | func is_class(type: String) -> bool: 97 | return type == "InkListDefinition" || .is_class(type) 98 | 99 | func get_class() -> String: 100 | return "InkListDefinition" 101 | 102 | func _to_string() -> String: 103 | return "[InkListDefinition \"%s\"]" % get_name() 104 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/state_patch.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkBase 13 | 14 | class_name InkStatePatch 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | var InkTryGetResult := preload("res://addons/inkgd/runtime/extra/try_get_result.gd") as GDScript 21 | var InkStringSet := preload("res://addons/inkgd/runtime/extra/string_set.gd") as GDScript 22 | 23 | # ############################################################################ # 24 | 25 | # Dictionary 26 | var globals: Dictionary setget , get_globals 27 | func get_globals() -> Dictionary: 28 | return _globals 29 | 30 | # StringSet 31 | var changed_variables: InkStringSet setget , get_changed_variables 32 | func get_changed_variables() -> InkStringSet: 33 | return _changed_variables 34 | 35 | # Dictionary 36 | var visit_counts: Dictionary setget , get_visit_counts 37 | func get_visit_counts() -> Dictionary: 38 | return _visit_counts 39 | 40 | # Dictionary 41 | var turn_indices setget , get_turn_indices 42 | func get_turn_indices() -> Dictionary: 43 | return _turn_indices 44 | 45 | # ############################################################################ # 46 | 47 | func _init(to_copy: InkStatePatch): 48 | if to_copy != null: 49 | _globals = to_copy._globals.duplicate() 50 | _changed_variables = to_copy._changed_variables.duplicate() 51 | _visit_counts = to_copy._visit_counts.duplicate() 52 | _turn_indices = to_copy._turn_indices.duplicate() 53 | else: 54 | _globals = {} 55 | _changed_variables = InkStringSet.new() 56 | _visit_counts = {} 57 | _turn_indices = {} 58 | 59 | # (String) -> { exists: bool, result: InkObject } 60 | func try_get_global(name) -> InkTryGetResult: 61 | if _globals.has(name): 62 | return InkTryGetResult.new(true, _globals[name]) 63 | 64 | return InkTryGetResult.new(false, null) 65 | 66 | func set_global(name: String, value: InkObject) -> void: 67 | _globals[name] = value 68 | 69 | func add_changed_variable(name: String) -> void: 70 | _changed_variables.append(name) 71 | 72 | # (InkContainer) -> { exists: bool, result: int } 73 | func try_get_visit_count(container) -> InkTryGetResult: 74 | if _visit_counts.has(container): 75 | return InkTryGetResult.new(true, _visit_counts[container]) 76 | 77 | return InkTryGetResult.new(false, 0) 78 | 79 | func set_visit_count(container: InkContainer, index: int) -> void: 80 | _visit_counts[container] = index 81 | 82 | func set_turn_index(container: InkContainer, index: int) -> void: 83 | _turn_indices[container] = index 84 | 85 | # (InkContainer) -> { exists: bool, result: int } 86 | func try_get_turn_index(container) -> InkTryGetResult: 87 | if _turn_indices.has(container): 88 | return InkTryGetResult.new(true, _turn_indices[container]) 89 | 90 | return InkTryGetResult.new(false, 0) 91 | 92 | var _globals: Dictionary 93 | var _changed_variables: InkStringSet 94 | var _visit_counts: Dictionary 95 | var _turn_indices: Dictionary 96 | 97 | # ############################################################################ # 98 | # GDScript extra methods 99 | # ############################################################################ # 100 | 101 | func is_class(type: String) -> bool: 102 | return type == "StatePatch" || .is_class(type) 103 | 104 | func get_class() -> String: 105 | return "StatePatch" 106 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/common/ink_rich_dialog.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | tool 8 | extends WindowDialog 9 | 10 | # A custom dialog showing a message and, optionally, a command output. 11 | 12 | # Hiding this type to prevent registration of "private" nodes. 13 | # See https://github.com/godotengine/godot-proposals/issues/1047 14 | # class_name InkRichDialog 15 | 16 | # ############################################################################ # 17 | # Nodes 18 | # ############################################################################ # 19 | 20 | onready var _margin_container = $MarginContainer 21 | onready var _vbox_container = $MarginContainer/VBoxContainer 22 | onready var _message_label = $MarginContainer/VBoxContainer/MessageLabel 23 | onready var _accept_button = $MarginContainer/VBoxContainer/AcceptButton 24 | onready var _output_panel = $MarginContainer/VBoxContainer/OutputPanel 25 | onready var _output_label = find_node("OutputLabel") 26 | 27 | # ############################################################################ # 28 | # Properties 29 | # ############################################################################ # 30 | 31 | ## The message displayed in the dialog. 32 | var message_text: String setget set_message_text, get_message_text 33 | func set_message_text(text: String): 34 | _message_label.text = text 35 | func get_message_text() -> String: 36 | return _message_label.text 37 | 38 | ## An output, often the result of a command, than can optionally be displayed 39 | ## in the dialog. 40 | ## 41 | ## Setting this property to null hides the corresponding panel in the dialog. 42 | var output_text: String setget set_output_text, get_output_text 43 | func set_output_text(text: String): 44 | _output_label.text = text 45 | _output_label.visible = !(text == null || text.length() == 0) 46 | func get_output_text() -> String: 47 | return _output_label.text 48 | 49 | # ############################################################################ # 50 | # Overriden Methods 51 | # ############################################################################ # 52 | 53 | func _ready(): 54 | _accept_button.connect("pressed", self, "_accept_button_pressed") 55 | 56 | var font = _get_source_font() 57 | if font != null: 58 | _output_panel.add_font_override("font", font) 59 | 60 | # ############################################################################ # 61 | # Methods 62 | # ############################################################################ # 63 | 64 | func update_layout(scale: float) -> void: 65 | _margin_container.add_constant_override("margin_right", 10 * scale) 66 | _margin_container.add_constant_override("margin_top", 10 * scale) 67 | _margin_container.add_constant_override("margin_left", 10 * scale) 68 | _margin_container.add_constant_override("margin_bottom", 10 * scale) 69 | _vbox_container.add_constant_override("separation", 10 * scale) 70 | 71 | 72 | # ############################################################################ # 73 | # Signal Receivers 74 | # ############################################################################ # 75 | 76 | func _accept_button_pressed(): 77 | self.get_parent().remove_child(self) 78 | self.queue_free() 79 | 80 | # ############################################################################ # 81 | # Private helpers 82 | # ############################################################################ # 83 | 84 | ## Gets the monospaced font used by the editor. 85 | func _get_source_font(): 86 | var theme = _retrieve_base_theme() 87 | if theme: 88 | return theme.get_font("output_source", "EditorFonts") 89 | else: 90 | return null 91 | 92 | ## Gets the theme currently used by the editor. 93 | func _retrieve_base_theme(): 94 | var parent: Control = self 95 | 96 | while(parent != null && parent.theme == null): 97 | var older_parent = parent.get_parent() 98 | if older_parent is Control: 99 | parent = older_parent 100 | else: 101 | break 102 | 103 | return parent.theme 104 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/structs/pointer.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | # ############################################################################ # 13 | # !! VALUE TYPE 14 | # ############################################################################ # 15 | 16 | # Pointers are passed around a lot, to prevent duplicating them all the time 17 | # and confusing the inspector when the debugger is attached, they are 18 | # immutable rather than being duplicated. 19 | 20 | extends InkBase 21 | 22 | class_name InkPointer 23 | 24 | # ############################################################################ # 25 | # Imports 26 | # ############################################################################ # 27 | 28 | var InkPath := preload("res://addons/inkgd/runtime/ink_path.gd") as GDScript 29 | 30 | static func InkPointer() -> GDScript: 31 | return load("res://addons/inkgd/runtime/structs/pointer.gd") as GDScript 32 | 33 | # ############################################################################ # 34 | 35 | # InkContainer 36 | # Encapsulating container into a weak ref. 37 | var container: InkContainer setget set_container, get_container 38 | func set_container(value: InkContainer) -> void: 39 | assert(false, "Pointer is immutable, cannot set container.") 40 | func get_container() -> InkContainer: 41 | return self._container.get_ref() 42 | var _container: WeakRef = WeakRef.new() 43 | 44 | var index: int setget set_index, get_index 45 | func set_index(value: int): 46 | assert(false, "Pointer is immutable, cannot set index.") 47 | func get_index() -> int: 48 | return _index 49 | var _index: int = 0 # int 50 | 51 | # (InkContainer, int) -> InkPointer 52 | func _init(container: InkContainer = null, index: int = 0): 53 | if container == null: 54 | self._container = WeakRef.new() 55 | else: 56 | self._container = weakref(container) 57 | 58 | self._index = index 59 | 60 | # () -> InkContainer 61 | func resolve(): 62 | if self.index < 0: return self.container 63 | if self.container == null: return null 64 | if self.container.content.size() == 0: return self.container 65 | if self.index >= self.container.content.size(): return null 66 | 67 | return self.container.content[self.index] 68 | 69 | # ############################################################################ # 70 | 71 | # () -> bool 72 | var is_null: bool setget , get_is_null 73 | func get_is_null() -> bool: 74 | return self.container == null 75 | 76 | # ############################################################################ # 77 | 78 | # TODO: Make inspectable 79 | # () -> InkPath 80 | var path: InkPath setget , get_path 81 | func get_path() -> InkPath: 82 | if self.is_null: 83 | return null 84 | 85 | if self.index >= 0: 86 | return self.container.path.path_by_appending_component( 87 | InkPath.Component.new(self.index) 88 | ) 89 | else: 90 | return self.container.path 91 | 92 | ############################################################################ # 93 | 94 | func _to_string() -> String: 95 | if self.container == null: 96 | return "Ink Pointer (null)" 97 | 98 | return "Ink Pointer -> %s -- index %d" % [self.container.path._to_string(), self.index] 99 | 100 | # (InkContainer) -> InkPointer 101 | static func start_of(container: InkContainer) -> InkPointer: 102 | return InkPointer().new(container, 0) 103 | 104 | # ############################################################################ # 105 | 106 | # () -> InkPointer 107 | static func null(): 108 | return InkPointer().new(null, -1) 109 | 110 | # ############################################################################ # 111 | # GDScript extra methods 112 | # ############################################################################ # 113 | 114 | func is_class(type: String) -> bool: 115 | return type == "Pointer" || .is_class(type) 116 | 117 | func get_class() -> String: 118 | return "Pointer" 119 | 120 | func duplicate() -> InkPointer: 121 | return InkPointer().new(self.container, self.index) 122 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/values/value.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | # This is a merge of the original Value class and its Value subclass. 15 | class_name InkValue 16 | 17 | # ############################################################################ # 18 | # IMPORTS 19 | # ############################################################################ # 20 | 21 | const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType 22 | var InkList = load("res://addons/inkgd/runtime/lists/ink_list.gd") 23 | 24 | # ############################################################################ # 25 | # STATIC REFERENCE 26 | # ############################################################################ # 27 | 28 | static func Utils(): 29 | return load("res://addons/inkgd/runtime/extra/utils.gd") 30 | 31 | static func Value(): 32 | return load("res://addons/inkgd/runtime/values/value.gd") 33 | 34 | static func BoolValue(): 35 | return load("res://addons/inkgd/runtime/values/bool_value.gd") 36 | 37 | static func IntValue(): 38 | return load("res://addons/inkgd/runtime/values/int_value.gd") 39 | 40 | static func FloatValue(): 41 | return load("res://addons/inkgd/runtime/values/float_value.gd") 42 | 43 | static func StringValue(): 44 | return load("res://addons/inkgd/runtime/values/string_value.gd") 45 | 46 | static func DivertTargetValue(): 47 | return load("res://addons/inkgd/runtime/values/divert_target_value.gd") 48 | 49 | static func VariablePointerValue(): 50 | return load("res://addons/inkgd/runtime/values/variable_pointer_value.gd") 51 | 52 | static func ListValue(): 53 | return load("res://addons/inkgd/runtime/values/list_value.gd") 54 | 55 | # ############################################################################ # 56 | 57 | var value # Variant 58 | 59 | # ValueType 60 | var value_type: int setget , get_value_type 61 | func get_value_type() -> int: 62 | return -1 63 | 64 | var is_truthy: bool setget , get_is_truthy 65 | func get_is_truthy() -> bool: 66 | return false 67 | 68 | # ############################################################################ # 69 | 70 | # (ValueType) -> ValueType 71 | func cast(new_type: int) -> InkValue: 72 | return null 73 | 74 | var value_object setget , get_value_object # Variant 75 | func get_value_object(): 76 | return value 77 | 78 | # ############################################################################ # 79 | 80 | # (Variant) -> Value 81 | func _init_with(val): 82 | value = val 83 | 84 | # (Variant) -> Value 85 | static func create(val) -> InkValue: 86 | # Original code lost precision from double to float. 87 | # But it's not applicable here. 88 | 89 | if val is bool: 90 | return BoolValue().new_with(val) 91 | if val is int: 92 | return IntValue().new_with(val) 93 | elif val is float: 94 | return FloatValue().new_with(val) 95 | elif val is String: 96 | return StringValue().new_with(val) 97 | elif Utils().is_ink_class(val, "InkPath"): 98 | return DivertTargetValue().new_with(val) 99 | elif Utils().is_ink_class(val, "InkList"): 100 | return ListValue().new_with(val) 101 | 102 | return null 103 | 104 | func copy() -> InkValue: 105 | return create(self.value_object) 106 | 107 | # (Ink.ValueType) -> StoryException 108 | func bad_cast_exception_message(target_class) -> String: 109 | return "Can't cast " + self.value_object + " from " + self.value_type + " to " + target_class 110 | 111 | # () -> String 112 | func _to_string() -> String: 113 | if value is int || value is float || value is String: 114 | return str(value) 115 | else: 116 | return value._to_string() 117 | 118 | # ############################################################################ # 119 | # GDScript extra methods 120 | # ############################################################################ # 121 | 122 | func is_class(type) -> bool: 123 | return type == "Value" || .is_class(type) 124 | 125 | func get_class() -> String: 126 | return "Value" 127 | 128 | static func new_with(val) -> InkValue: 129 | var value = Value().new() 130 | value._init_with(val) 131 | return value 132 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/ink_bottom_panel.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:return_value_discarded 2 | 3 | # ############################################################################ # 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # Licensed under the MIT License. 6 | # See LICENSE in the project root for license information. 7 | # ############################################################################ # 8 | 9 | tool 10 | extends Control 11 | 12 | # Hiding this type to prevent registration of "private" nodes. 13 | # See https://github.com/godotengine/godot-proposals/issues/1047 14 | # class_name InkBottomPanel 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | var InkStoryPanelScene = load("res://addons/inkgd/editor/panel/stories/ink_story_panel.tscn") 21 | var InkPreviewPanelScene = load("res://addons/inkgd/editor/panel/preview/ink_preview_panel.tscn") 22 | var InkConfigurationPanelScene = load("res://addons/inkgd/editor/panel/configuration/ink_configuration_panel.tscn") 23 | 24 | # ############################################################################ # 25 | # Properties 26 | # ############################################################################ # 27 | 28 | var editor_interface: InkEditorInterface = null 29 | var configuration: InkConfiguration = null 30 | 31 | # ############################################################################ # 32 | # Private Properties 33 | # ############################################################################ # 34 | 35 | var _progress_texture: AnimatedTexture 36 | 37 | # ############################################################################ # 38 | # Hierarchy Nodes 39 | # ############################################################################ # 40 | 41 | onready var _tab_container: TabContainer = $TabContainer 42 | onready var _beta_button: LinkButton = $MarginContainer/LinkButton 43 | 44 | onready var _story_panel = InkStoryPanelScene.instance() 45 | onready var _preview_panel = InkPreviewPanelScene.instance() 46 | onready var _configuration_panel = InkConfigurationPanelScene.instance() 47 | 48 | # ############################################################################ # 49 | # Overrides 50 | # ############################################################################ # 51 | 52 | func _ready(): 53 | # FIXME: This needs investigating. 54 | # Sanity check. It seems the editor instantiates tools script on their 55 | # own, probably to add them to its tree. In that case, they won't have 56 | # their dependencies injected, so we're not doing anything. 57 | if editor_interface == null || configuration == null: 58 | print("[inkgd] [INFO] Ink Bottom Panel: dependencies not met, ignoring.") 59 | return 60 | 61 | _progress_texture = _create_progress_texture() 62 | 63 | _story_panel.editor_interface = editor_interface 64 | _story_panel.configuration = configuration 65 | _story_panel.progress_texture = _progress_texture 66 | 67 | _preview_panel.editor_interface = editor_interface 68 | _preview_panel.configuration = configuration 69 | _preview_panel.progress_texture = _progress_texture 70 | 71 | _configuration_panel.editor_interface = editor_interface 72 | _configuration_panel.configuration = configuration 73 | 74 | _tab_container.add_child(_story_panel) 75 | _tab_container.add_child(_preview_panel) 76 | _tab_container.add_child(_configuration_panel) 77 | 78 | _beta_button.connect("pressed", self, "_open_github_issues") 79 | 80 | _set_minimum_panel_size() 81 | 82 | 83 | # ############################################################################ # 84 | # Signals Receivers 85 | # ############################################################################ # 86 | 87 | func _open_github_issues(): 88 | OS.shell_open("https://github.com/ephread/inkgd/issues/new?assignees=&labels=&template=bug_report.md") 89 | 90 | # ############################################################################ # 91 | # Private helpers 92 | # ############################################################################ # 93 | 94 | func _create_progress_texture() -> AnimatedTexture: 95 | var animated_texture = AnimatedTexture.new() 96 | animated_texture.frames = 8 97 | 98 | for index in range(8): 99 | var texture = get_icon(str("Progress", (index + 1)), "EditorIcons") 100 | animated_texture.set_frame_texture(index, texture) 101 | 102 | return animated_texture 103 | 104 | func _set_minimum_panel_size(): 105 | # Adapting the minimum size of the panel to the scale of the editor. 106 | rect_min_size = Vector2(900, 245) * editor_interface.scale 107 | -------------------------------------------------------------------------------- /examples/scenes/common/story_player.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=14 format=2] 2 | 3 | [ext_resource path="res://examples/scenes/common/story_player.gd" type="Script" id=1] 4 | [ext_resource path="res://examples/fonts/Poppins-Medium.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://examples/images/spinner.png" type="Texture" id=4] 6 | [ext_resource path="res://examples/images/transparent.png" type="Texture" id=5] 7 | 8 | [sub_resource type="StyleBoxFlat" id=1] 9 | bg_color = Color( 0, 0, 0, 1 ) 10 | 11 | [sub_resource type="StyleBoxFlat" id=2] 12 | bg_color = Color( 0.145098, 0.145098, 0.145098, 1 ) 13 | 14 | [sub_resource type="StyleBoxFlat" id=3] 15 | bg_color = Color( 0.270588, 0.270588, 0.270588, 1 ) 16 | 17 | [sub_resource type="StyleBoxEmpty" id=4] 18 | 19 | [sub_resource type="StyleBoxEmpty" id=5] 20 | 21 | [sub_resource type="Theme" id=6] 22 | VScrollBar/icons/decrement = ExtResource( 5 ) 23 | VScrollBar/icons/decrement_highlight = ExtResource( 5 ) 24 | VScrollBar/icons/increment = ExtResource( 5 ) 25 | VScrollBar/icons/increment_highlight = ExtResource( 5 ) 26 | VScrollBar/styles/grabber = SubResource( 1 ) 27 | VScrollBar/styles/grabber_highlight = SubResource( 2 ) 28 | VScrollBar/styles/grabber_pressed = SubResource( 3 ) 29 | VScrollBar/styles/scroll = SubResource( 4 ) 30 | VScrollBar/styles/scroll_focus = SubResource( 5 ) 31 | 32 | [sub_resource type="Animation" id=7] 33 | loop = true 34 | tracks/0/type = "value" 35 | tracks/0/path = NodePath("LoadingAnimationPlayer/CenterContainer/VBoxContainer/CenterContainer/SpinnerTextureRect:rect_rotation") 36 | tracks/0/interp = 1 37 | tracks/0/loop_wrap = true 38 | tracks/0/imported = false 39 | tracks/0/enabled = true 40 | tracks/0/keys = { 41 | "times": PoolRealArray( 0, 1 ), 42 | "transitions": PoolRealArray( 1, 1 ), 43 | "update": 0, 44 | "values": [ 0.0, 360.0 ] 45 | } 46 | 47 | [sub_resource type="DynamicFont" id=9] 48 | size = 55 49 | font_data = ExtResource( 2 ) 50 | 51 | [sub_resource type="Theme" id=8] 52 | Label/colors/font_color = Color( 0, 0, 0, 1 ) 53 | Label/fonts/font = SubResource( 9 ) 54 | 55 | [node name="StoryPlayer" type="Control"] 56 | anchor_right = 1.0 57 | anchor_bottom = 1.0 58 | margin_left = 1.0 59 | margin_right = 1.0 60 | script = ExtResource( 1 ) 61 | __meta__ = { 62 | "_edit_use_anchors_": false 63 | } 64 | bind_externals = true 65 | 66 | [node name="ColorRect" type="ColorRect" parent="."] 67 | anchor_right = 1.0 68 | anchor_bottom = 1.0 69 | color = Color( 0.929412, 0.898039, 0.854902, 1 ) 70 | __meta__ = { 71 | "_edit_use_anchors_": false 72 | } 73 | 74 | [node name="StoryMarginContainer" type="MarginContainer" parent="."] 75 | anchor_right = 1.0 76 | anchor_bottom = 1.0 77 | custom_constants/margin_right = 250 78 | custom_constants/margin_top = 40 79 | custom_constants/margin_left = 250 80 | custom_constants/margin_bottom = 40 81 | 82 | [node name="StoryScrollContainer" type="ScrollContainer" parent="StoryMarginContainer"] 83 | margin_left = 250.0 84 | margin_top = 40.0 85 | margin_right = 1670.0 86 | margin_bottom = 1040.0 87 | grow_horizontal = 2 88 | theme = SubResource( 6 ) 89 | scroll_horizontal_enabled = false 90 | 91 | [node name="StoryVBoxContainer" type="VBoxContainer" parent="StoryMarginContainer/StoryScrollContainer"] 92 | margin_right = 1420.0 93 | grow_horizontal = 2 94 | size_flags_horizontal = 3 95 | custom_constants/separation = 20 96 | 97 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 98 | anchor_left = 1.0 99 | anchor_right = 1.0 100 | anchor_bottom = 1.0 101 | margin_left = -113.0 102 | __meta__ = { 103 | "_edit_use_anchors_": false 104 | } 105 | 106 | [node name="LoadingAnimationPlayer" type="AnimationPlayer" parent="."] 107 | autoplay = "LoadingAnimation" 108 | anims/LoadingAnimation = SubResource( 7 ) 109 | 110 | [node name="CenterContainer" type="CenterContainer" parent="LoadingAnimationPlayer"] 111 | anchor_right = 1.0 112 | anchor_bottom = 1.0 113 | __meta__ = { 114 | "_edit_use_anchors_": false 115 | } 116 | 117 | [node name="VBoxContainer" type="VBoxContainer" parent="LoadingAnimationPlayer/CenterContainer"] 118 | margin_left = 776.0 119 | margin_top = 449.0 120 | margin_right = 1143.0 121 | margin_bottom = 631.0 122 | custom_constants/separation = 40 123 | 124 | [node name="TitleLabel" type="Label" parent="LoadingAnimationPlayer/CenterContainer/VBoxContainer"] 125 | margin_right = 367.0 126 | margin_bottom = 78.0 127 | theme = SubResource( 8 ) 128 | text = "The Intercept" 129 | 130 | [node name="CenterContainer" type="CenterContainer" parent="LoadingAnimationPlayer/CenterContainer/VBoxContainer"] 131 | margin_top = 118.0 132 | margin_right = 367.0 133 | margin_bottom = 182.0 134 | 135 | [node name="SpinnerTextureRect" type="TextureRect" parent="LoadingAnimationPlayer/CenterContainer/VBoxContainer/CenterContainer"] 136 | margin_left = 151.0 137 | margin_right = 215.0 138 | margin_bottom = 64.0 139 | rect_pivot_offset = Vector2( 32, 32 ) 140 | texture = ExtResource( 4 ) 141 | stretch_mode = 4 142 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/content/divert.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | extends InkObject 13 | 14 | class_name InkDivert 15 | 16 | # ############################################################################ # 17 | # Imports 18 | # ############################################################################ # 19 | 20 | const PushPopType = preload("res://addons/inkgd/runtime/enums/push_pop.gd").PushPopType 21 | var InkPointer := load("res://addons/inkgd/runtime/structs/pointer.gd") as GDScript 22 | 23 | # ############################################################################ # 24 | 25 | var target_path: InkPath setget set_target_path, get_target_path 26 | func get_target_path() -> InkPath: 27 | if self._target_path != null && self._target_path.is_relative: 28 | var target_obj: InkObject = self.target_pointer.resolve() 29 | if target_obj: 30 | self._target_path = target_obj.path 31 | 32 | return self._target_path 33 | 34 | func set_target_path(value: InkPath): 35 | self._target_path = value 36 | self._target_pointer = InkPointer.null() 37 | 38 | # InkPath 39 | var _target_path = null 40 | 41 | var target_pointer: InkPointer setget , get_target_pointer # InkPointer 42 | func get_target_pointer() -> InkPointer: 43 | if self._target_pointer.is_null: 44 | var target_obj = resolve_path(self._target_path).obj 45 | 46 | if self._target_path.last_component.is_index: 47 | self._target_pointer = InkPointer.new( 48 | Utils.as_or_null(target_obj.parent, "InkContainer"), 49 | self._target_path.last_component.index 50 | ) 51 | else: 52 | self._target_pointer = InkPointer.start_of(Utils.as_or_null(target_obj, "InkContainer")) 53 | 54 | return self._target_pointer 55 | 56 | var _target_pointer: InkPointer = InkPointer.null() 57 | 58 | # String? 59 | var target_path_string setget set_target_path_string, get_target_path_string 60 | func get_target_path_string(): 61 | if self.target_path == null: 62 | return null 63 | 64 | return self.compact_path_string(self.target_path) 65 | 66 | func set_target_path_string(value): 67 | if value == null: 68 | self.target_path = null 69 | else: 70 | self.target_path = InkPath().new_with_components_string(value) 71 | 72 | # String 73 | var variable_divert_name = null 74 | var has_variable_target: bool setget , get_has_variable_target 75 | func get_has_variable_target() -> bool: 76 | return self.variable_divert_name != null 77 | 78 | var pushes_to_stack: bool = false 79 | 80 | # PushPopType 81 | var stack_push_type: int = 0 82 | 83 | var is_external: bool = false 84 | var external_args: int = 0 85 | 86 | var is_conditional: bool = false 87 | 88 | 89 | # (int?) -> InkDivert 90 | func _init_with(stack_push_type = null): 91 | self.pushes_to_stack = false 92 | 93 | if stack_push_type != null: 94 | self.pushes_to_stack = true 95 | self.stack_push_type = stack_push_type 96 | 97 | # (InkBase) -> bool 98 | func equals(obj) -> bool: 99 | var other_divert: InkDivert = Utils.as_or_null(obj, "Divert") 100 | if other_divert: 101 | if self.has_variable_target == other_divert.has_variable_target: 102 | if self.has_variable_target: 103 | return self.variable_divert_name == other_divert.variable_divert_name 104 | else: 105 | return self.target_path.equals(other_divert.target_path) 106 | 107 | return false 108 | 109 | func _to_string() -> String: 110 | if self.has_variable_target: 111 | return "Divert(variable: %s)" % self.variable_divert_name 112 | elif self.target_path == null: 113 | return "Divert(null)" 114 | else: 115 | var _string = "" 116 | 117 | var target_str: String = self.target_path._to_string() 118 | var target_line_num = debug_line_number_of_path(self.target_path) 119 | if target_line_num != null: 120 | target_str = "line " + target_line_num 121 | 122 | _string += "Divert" 123 | 124 | if self.is_conditional: 125 | _string += "?" 126 | 127 | if self.pushes_to_stack: 128 | if self.stack_push_type == PushPopType.FUNCTION: 129 | _string += " function" 130 | else: 131 | _string += " tunnel" 132 | 133 | _string += " -> " 134 | _string += self.target_path_string 135 | 136 | _string += " (%s)" % target_str 137 | 138 | return _string 139 | 140 | # ############################################################################ # 141 | # GDScript extra methods 142 | # ############################################################################ # 143 | 144 | func is_class(type: String) -> bool: 145 | return type == "Divert" || .is_class(type) 146 | 147 | func get_class() -> String: 148 | return "Divert" 149 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/stories/ink_story_panel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/inkgd/editor/panel/stories/ink_story_panel.gd" type="Script" id=1] 4 | 5 | [sub_resource type="Image" id=3] 6 | data = { 7 | "data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), 8 | "format": "LumAlpha8", 9 | "height": 16, 10 | "mipmaps": false, 11 | "width": 16 12 | } 13 | 14 | [sub_resource type="ImageTexture" id=2] 15 | flags = 4 16 | flags = 4 17 | image = SubResource( 3 ) 18 | size = Vector2( 16, 16 ) 19 | 20 | [node name="Story" type="Control"] 21 | anchor_right = 1.0 22 | anchor_bottom = 1.0 23 | script = ExtResource( 1 ) 24 | __meta__ = { 25 | "_edit_use_anchors_": false 26 | } 27 | 28 | [node name="ScrollContainer" type="ScrollContainer" parent="."] 29 | anchor_right = 1.0 30 | anchor_bottom = 1.0 31 | 32 | [node name="StoriesVBoxContainer" type="VBoxContainer" parent="ScrollContainer"] 33 | margin_right = 1920.0 34 | margin_bottom = 36.0 35 | size_flags_horizontal = 3 36 | __meta__ = { 37 | "_edit_use_anchors_": false 38 | } 39 | 40 | [node name="HBoxContainer" type="HBoxContainer" parent="ScrollContainer/StoriesVBoxContainer"] 41 | margin_right = 1920.0 42 | margin_bottom = 22.0 43 | alignment = 2 44 | __meta__ = { 45 | "_edit_use_anchors_": false 46 | } 47 | 48 | [node name="Label" type="Label" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"] 49 | margin_top = 4.0 50 | margin_right = 1718.0 51 | margin_bottom = 18.0 52 | size_flags_horizontal = 3 53 | text = "Managed Stories" 54 | 55 | [node name="AddNewStoryButton" type="Button" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"] 56 | margin_left = 1722.0 57 | margin_right = 1822.0 58 | margin_bottom = 22.0 59 | mouse_filter = 1 60 | custom_constants/hseparation = 8 61 | text = "New story" 62 | icon = SubResource( 2 ) 63 | __meta__ = { 64 | "_editor_description_": "Compile the story manually, based on the configuration below." 65 | } 66 | 67 | [node name="VSeparator" type="VSeparator" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"] 68 | margin_left = 1826.0 69 | margin_right = 1830.0 70 | margin_bottom = 22.0 71 | 72 | [node name="BuildAllButton" type="Button" parent="ScrollContainer/StoriesVBoxContainer/HBoxContainer"] 73 | margin_left = 1834.0 74 | margin_right = 1920.0 75 | margin_bottom = 22.0 76 | mouse_filter = 1 77 | custom_constants/hseparation = 10 78 | text = "Compile All" 79 | __meta__ = { 80 | "_editor_description_": "Compile the story manually, based on the configuration below." 81 | } 82 | 83 | [node name="MarginContainer" type="MarginContainer" parent="ScrollContainer/StoriesVBoxContainer"] 84 | margin_top = 26.0 85 | margin_right = 1920.0 86 | margin_bottom = 36.0 87 | 88 | [node name="Panel" type="Panel" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer"] 89 | self_modulate = Color( 1, 1, 1, 0.686275 ) 90 | margin_right = 1920.0 91 | margin_bottom = 10.0 92 | __meta__ = { 93 | "_edit_use_anchors_": false 94 | } 95 | 96 | [node name="MarginContainer" type="MarginContainer" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer"] 97 | margin_right = 1920.0 98 | margin_bottom = 10.0 99 | custom_constants/margin_right = 5 100 | custom_constants/margin_top = 5 101 | custom_constants/margin_left = 5 102 | custom_constants/margin_bottom = 5 103 | __meta__ = { 104 | "_edit_use_anchors_": false 105 | } 106 | 107 | [node name="StoryConfigurationVBoxContainer" type="VBoxContainer" parent="ScrollContainer/StoriesVBoxContainer/MarginContainer/MarginContainer"] 108 | margin_left = 5.0 109 | margin_top = 5.0 110 | margin_right = 1915.0 111 | margin_bottom = 5.0 112 | size_flags_horizontal = 3 113 | custom_constants/separation = 15 114 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/flow.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # ############################################################################ # 3 | # Copyright © 2015-2021 inkle Ltd. 4 | # Copyright © 2019-2022 Frédéric Maquin 5 | # All Rights Reserved 6 | # 7 | # This file is part of inkgd. 8 | # inkgd is licensed under the terms of the MIT license. 9 | # ############################################################################ # 10 | 11 | extends InkBase 12 | 13 | class_name InkFlow 14 | 15 | # ############################################################################ # 16 | # Imports 17 | # ############################################################################ # 18 | 19 | var CallStack = load("res://addons/inkgd/runtime/callstack.gd") 20 | 21 | # ############################################################################ # 22 | # Self-reference 23 | # ############################################################################ # 24 | 25 | static func Flow(): 26 | return load("res://addons/inkgd/runtime/flow.gd") 27 | 28 | # ############################################################################ # 29 | 30 | var name # string 31 | var callstack # CallStack 32 | var output_stream # Array 33 | var current_choices # Array 34 | 35 | func _init(): 36 | get_static_json() 37 | 38 | # (String, Story) -> Flow 39 | func _init_with_name(name, story): 40 | self.name = name 41 | self.callstack = CallStack.new(story) 42 | self.output_stream = [] 43 | self.current_choices = [] 44 | 45 | # (String, Story, Dictionary) -> Flow 46 | func _init_with_name_and_jobject(name, story, jobject): 47 | self.name = name 48 | self.callstack = CallStack.new(story) 49 | self.callstack.set_json_token(jobject["callstack"], story) 50 | self.output_stream = self.Json.jarray_to_runtime_obj_list(jobject["outputStream"]) 51 | self.current_choices = self.Json.jarray_to_runtime_obj_list(jobject["currentChoices"]) 52 | 53 | # jchoice_threads_obj is null if 'choiceThreads' doesn't exist. 54 | var jchoice_threads_obj = jobject.get("choiceThreads"); 55 | self.load_flow_choice_threads(jchoice_threads_obj, story) 56 | 57 | # (SimpleJson.Writer) -> void 58 | func write_json(writer): 59 | writer.write_object_start() 60 | writer.write_property("callstack", funcref(self.callstack, "write_json")) 61 | writer.write_property( 62 | "outputStream", 63 | funcref(self, "_anonymous_write_property_output_stream") 64 | ) 65 | 66 | var has_choice_threads = false 67 | for c in self.current_choices: 68 | c.original_thread_index = c.thread_at_generation.thread_index 69 | 70 | if self.callstack.thread_with_index(c.original_thread_index) == null: 71 | if !has_choice_threads: 72 | has_choice_threads = true 73 | writer.write_property_start("choiceThreads") 74 | writer.write_object_start() 75 | 76 | writer.write_property_start(c.original_thread_index) 77 | c.thread_at_generation.write_json(writer) 78 | writer.write_property_end() 79 | 80 | if has_choice_threads: 81 | writer.write_object_end() 82 | writer.write_property_end() 83 | 84 | writer.write_property( 85 | "currentChoices", 86 | funcref(self, "_anonymous_write_property_current_choices") 87 | ) 88 | 89 | writer.write_object_end() 90 | 91 | # (Dictionary, Story) -> void 92 | func load_flow_choice_threads(jchoice_threads, story): 93 | for choice in self.current_choices: 94 | var found_active_thread = self.callstack.thread_with_index(choice.original_thread_index) 95 | if found_active_thread != null: 96 | choice.thread_at_generation = found_active_thread.copy() 97 | else: 98 | var jsaved_choice_thread = jchoice_threads[str(choice.original_thread_index)] 99 | choice.thread_at_generation = CallStack.InkThread.new_with(jsaved_choice_thread, story) 100 | 101 | # (SimpleJson.Writer) -> void 102 | func _anonymous_write_property_output_stream(w): 103 | self.Json.write_list_runtime_objs(w, self.output_stream) 104 | 105 | # (SimpleJson.Writer) -> void 106 | func _anonymous_write_property_current_choices(w): 107 | w.write_array_start() 108 | for c in self.current_choices: 109 | self.Json.write_choice(w, c) 110 | w.write_array_end() 111 | 112 | func equals(ink_base) -> bool: 113 | return false 114 | 115 | func _to_string() -> String: 116 | return str(self) 117 | 118 | # ############################################################################ # 119 | # GDScript extra methods 120 | # ############################################################################ # 121 | 122 | func is_class(type): 123 | return type == "Flow" || .is_class(type) 124 | 125 | func get_class(): 126 | return "Flow" 127 | 128 | static func new_with_name(name, story): 129 | var flow = Flow().new() 130 | flow._init_with_name(name, story) 131 | return flow 132 | 133 | static func new_with_name_and_jobject(name, story, jobject): 134 | var flow = Flow().new() 135 | flow._init_with_name_and_jobject(name, story, jobject) 136 | return flow 137 | 138 | # ############################################################################ # 139 | var Json setget , get_Json 140 | func get_Json(): 141 | return _Json.get_ref() 142 | 143 | var _Json = WeakRef.new() 144 | 145 | func get_static_json(): 146 | var InkRuntime = Engine.get_main_loop().root.get_node("__InkRuntime") 147 | 148 | Utils.__assert__(InkRuntime != null, 149 | str("Could not retrieve 'InkRuntime' singleton from the scene tree.")) 150 | 151 | _Json = weakref(InkRuntime.json) 152 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/ink_compiler.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends InkExternalCommandExecutor 8 | 9 | class_name InkCompiler 10 | 11 | # ############################################################################ # 12 | # Imports 13 | # ############################################################################ # 14 | 15 | var InkExecutionResult = load("res://addons/inkgd/editor/common/executors/structures/ink_execution_result.gd") 16 | 17 | # ############################################################################ # 18 | # Private Properties 19 | # ############################################################################ # 20 | 21 | ## Ink Configuration 22 | var _configuration: InkCompilationConfiguration 23 | 24 | # ############################################################################ # 25 | # Signals 26 | # ############################################################################ # 27 | 28 | ## Emitted when a compilation completed. Note that this doesn't imply that 29 | ## the compulation was successful. Check the content of result 30 | ## (InkCompiler.Result) for more information. 31 | signal story_compiled(result) 32 | 33 | # ############################################################################ # 34 | # Overrides 35 | # ############################################################################ # 36 | 37 | func _init(configuration: InkCompilationConfiguration): 38 | _configuration = configuration 39 | 40 | if _configuration.use_threads: 41 | _thread = Thread.new() 42 | 43 | # ############################################################################ # 44 | # Methods 45 | # ############################################################################ # 46 | 47 | ## Compile the story, based on the compilation configuration provided 48 | ## by this object. If `configuration.use_thread` is set to `false`, 49 | ## this method will return `true` if the compilation succeeded and `false` 50 | ## otherwise. If `configuration.use_thread` is set to `true`, this method 51 | ## always returns `true`. 52 | func compile_story() -> bool: 53 | if _configuration.use_threads: 54 | var error = _thread.start(self, "_compile_story", _configuration, Thread.PRIORITY_HIGH) 55 | 56 | if error != OK: 57 | var result = InkExecutionResult.new( 58 | self.identifier, 59 | _configuration.use_threads, 60 | _configuration.user_triggered, 61 | false, 62 | "" 63 | ) 64 | 65 | call_deferred("emit_signal", "story_compiled", result) 66 | 67 | return true 68 | else: 69 | return _compile_story(_configuration) 70 | 71 | # ############################################################################ # 72 | # Private Helpers 73 | # ############################################################################ # 74 | 75 | ## Compile the story, based on the given compilation configuration 76 | ## If `configuration.use_thread` is set to `false`, this method will 77 | ## return `true` if the compilation succeeded and `false` otherwise. 78 | ## If `configuration.use_thread` is set to `false`, this method always 79 | ## returns `true`. 80 | func _compile_story(config: InkCompilationConfiguration) -> bool: 81 | print("[inkgd] [INFO] Executing compilation command…") 82 | var return_code = 0 83 | var output = [] 84 | 85 | var start_time = OS.get_ticks_msec() 86 | 87 | if config.use_mono: 88 | var args = [config.inklecate_path, '-o', config.target_file_path, config.source_file_path] 89 | return_code = OS.execute(config.mono_path, args, true, output, true) 90 | else: 91 | var args = ['-o', config.target_file_path, config.source_file_path] 92 | return_code = OS.execute(config.inklecate_path, args, true, output, true) 93 | 94 | var end_time = OS.get_ticks_msec() 95 | 96 | print("[inkgd] [INFO] Command executed in %dms." % (end_time - start_time)) 97 | 98 | var string_output = PoolStringArray(output) 99 | if _configuration.use_threads: 100 | call_deferred("_handle_compilation_result", config, return_code, string_output) 101 | return true 102 | else: 103 | var result = _process_compilation_result(config, return_code, string_output) 104 | return result.success 105 | 106 | 107 | ## Handles the compilation results when exectuted in a different thread. 108 | ## 109 | ## This method should always be executed on the main thread. 110 | func _handle_compilation_result( 111 | config: InkCompilationConfiguration, 112 | return_code: int, 113 | output: Array 114 | ): 115 | _thread.wait_to_finish() 116 | 117 | var result = _process_compilation_result(config, return_code, output) 118 | emit_signal("story_compiled", result) 119 | 120 | 121 | ## Process the compilation results turning them into an instance of `Result`. 122 | ## 123 | ## This method will also print to the editor's output panel. 124 | func _process_compilation_result( 125 | config: InkCompilationConfiguration, 126 | return_code: int, 127 | output: PoolStringArray 128 | ) -> InkExecutionResult: 129 | var success: bool = (return_code == 0) 130 | var output_text: String = output.join("\n").replace(BOM, "").strip_edges() 131 | 132 | if success: 133 | print("[inkgd] [INFO] %s was successfully compiled." % config.source_file_path) 134 | if output_text.length() > 0: 135 | print(output_text) 136 | else: 137 | printerr("[inkgd] [ERROR] Could not compile %s." % config.source_file_path) 138 | printerr(output_text) 139 | 140 | return InkExecutionResult.new( 141 | self.identifier, 142 | config.use_threads, 143 | config.user_triggered, 144 | success, 145 | output_text 146 | ) 147 | -------------------------------------------------------------------------------- /addons/inkgd/runtime/lists/structs/ink_list_item.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore-all:shadowed_variable 2 | # warning-ignore-all:unused_class_variable 3 | # ############################################################################ # 4 | # Copyright © 2015-2021 inkle Ltd. 5 | # Copyright © 2019-2022 Frédéric Maquin 6 | # All Rights Reserved 7 | # 8 | # This file is part of inkgd. 9 | # inkgd is licensed under the terms of the MIT license. 10 | # ############################################################################ # 11 | 12 | # ############################################################################ # 13 | # !! VALUE TYPE 14 | # ############################################################################ # 15 | 16 | extends InkObject 17 | 18 | class_name InkListItem 19 | 20 | # ############################################################################ # 21 | # Self-reference 22 | # ############################################################################ # 23 | 24 | static func InkListItem(): 25 | return load("res://addons/inkgd/runtime/lists/structs/ink_list_item.gd") 26 | 27 | # ############################################################################ # 28 | 29 | # Originally these were simple variables, but they are turned into properties to 30 | # make the object "immutable". That way it can be passed around without being 31 | # duplicated. 32 | 33 | var origin_name setget , get_origin_name 34 | func get_origin_name(): 35 | return _origin_name 36 | var _origin_name = null # String 37 | 38 | var item_name setget , get_item_name 39 | func get_item_name(): 40 | return _item_name 41 | var _item_name = null # String 42 | 43 | # ############################################################################ # 44 | 45 | # (string, string) -> InkListItem 46 | func _init_with_origin_name(origin_name, item_name): 47 | self._origin_name = origin_name 48 | self._item_name = item_name 49 | 50 | # (string) -> InkListItem 51 | func _init_with_full_name(full_name): 52 | var name_parts = full_name.split(".") 53 | self._origin_name = name_parts[0] 54 | self._item_name = name_parts[1] 55 | 56 | static func null() -> InkListItem: 57 | return InkListItem().new_with_origin_name(null, null) 58 | 59 | # ############################################################################ # 60 | 61 | var is_null: bool setget , get_is_null 62 | func get_is_null() -> bool: 63 | return self.origin_name == null && self.item_name == null 64 | 65 | # String 66 | var full_name setget , get_full_name 67 | func get_full_name(): 68 | # In C#, concatenating null produce nothing, in GDScript, it appends "Null". 69 | return ( 70 | (self.origin_name if self.origin_name else "?") + "." + 71 | (self.item_name if self.item_name else "") 72 | ) 73 | 74 | # ############################################################################ # 75 | 76 | # () -> String 77 | func _to_string() -> String: 78 | return self.full_name 79 | 80 | # (InkObject) -> bool 81 | func equals(obj: InkObject) -> bool: 82 | if obj.is_class("InkListItem"): 83 | var other_item = obj 84 | return ( 85 | other_item.item_name == self.item_name && 86 | self.other_item.origin_name == self.origin_name 87 | ) 88 | 89 | return false 90 | 91 | # ############################################################################ # 92 | 93 | # (string, string) -> InkListItem 94 | static func new_with_origin_name(origin_name, item_name) -> InkListItem: 95 | var list_item = InkListItem().new() 96 | list_item._init_with_origin_name(origin_name, item_name) 97 | return list_item 98 | 99 | # (string) -> InkListItem 100 | static func new_with_full_name(full_name) -> InkListItem: 101 | var list_item = InkListItem().new() 102 | list_item._init_with_full_name(full_name) 103 | return list_item 104 | 105 | # ############################################################################ # 106 | # GDScript extra methods 107 | # ############################################################################ # 108 | 109 | func is_class(type: String) -> bool: 110 | return type == "InkListItem" || .is_class(type) 111 | 112 | func get_class() -> String: 113 | return "InkListItem" 114 | 115 | # ############################################################################ # 116 | # These methods did not exist in the original C# code. Their purpose is to 117 | # make `InkListItem` mimic the value-type semantics of the original 118 | # struct, as well as offering a serialization mechanism to use `InkListItem` 119 | # as keys in dictionaries. 120 | 121 | # Returns a `SerializedInkListItem` representing the current 122 | # instance. The result is intended to be used as a key inside a Map. 123 | func serialized() -> String: 124 | # We are simply using a JSON representation as a value-typed key. 125 | var json_print = JSON.print( 126 | { "originName": self.origin_name, "itemName": self.item_name } 127 | ) 128 | return json_print 129 | 130 | # Reconstructs a `InkListItem` from the given SerializedInkListItem. 131 | # 132 | # (String) -> InkListItem 133 | static func from_serialized_key(key: String) -> InkListItem: 134 | var obj = JSON.parse(key).result 135 | if !InkListItem()._is_like_ink_list_item(obj): 136 | return InkListItem().null() 137 | 138 | return InkListItem().new_with_origin_name(obj["originName"], obj["itemName"]) 139 | 140 | # Determines whether the given item is sufficiently `InkListItem`-like 141 | # to be used as a template when reconstructing the InkListItem. 142 | # 143 | # (Variant) -> bool 144 | static func _is_like_ink_list_item(item) -> bool: 145 | if !(item is Dictionary): 146 | return false 147 | 148 | if !(item.has("originName") && item.has("itemName")): 149 | return false 150 | 151 | if !(item["originName"] is String): 152 | return false 153 | 154 | if !(item["itemName"] is String): 155 | return false 156 | 157 | return true 158 | -------------------------------------------------------------------------------- /addons/inkgd/editor/panel/stories/ink_story_configuration.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | tool 8 | extends VBoxContainer 9 | 10 | # Hiding this type to prevent registration of "private" nodes. 11 | # See https://github.com/godotengine/godot-proposals/issues/1047 12 | # class_name InkStoryConfiguration 13 | 14 | # ############################################################################ # 15 | # Signals 16 | # ############################################################################ # 17 | 18 | signal configuration_changed(story_configuration) 19 | 20 | signal remove_button_pressed(story_configuration) 21 | 22 | signal build_button_pressed(story_configuration) 23 | 24 | signal source_file_button_pressed(story_configuration) 25 | 26 | signal target_file_button_pressed(story_configuration) 27 | 28 | signal watched_folder_button_pressed(story_configuration) 29 | 30 | # ############################################################################ # 31 | # Properties 32 | # ############################################################################ # 33 | 34 | var editor_interface: InkEditorInterface = null 35 | 36 | # ############################################################################ # 37 | # Nodes 38 | # ############################################################################ # 39 | 40 | onready var story_label = find_node("StoryLabel") 41 | 42 | onready var remove_button = find_node("RemoveButton") 43 | onready var build_button = find_node("BuildButton") 44 | 45 | onready var source_file_line_edit = find_node("SourceFileLineEdit") 46 | onready var source_file_dialog_button = find_node("SourceFileDialogButton") 47 | 48 | onready var target_file_line_edit = find_node("TargetFileLineEdit") 49 | onready var target_file_dialog_button = find_node("TargetFileDialogButton") 50 | 51 | onready var watched_folder_label = find_node("WatchedFolderLabel") 52 | onready var watched_folder_container = find_node("WatchedFolderHBoxContainer") 53 | onready var watched_folder_line_edit = find_node("WatchedFolderLineEdit") 54 | onready var watched_folder_dialog_button = find_node("WatchedFolderDialogButton") 55 | 56 | onready var background_color_rect = find_node("BackgroundColorRect") 57 | 58 | # ############################################################################ # 59 | # Overrides 60 | # ############################################################################ # 61 | 62 | func _ready(): 63 | # FIXME: This needs investigating. 64 | # Sanity check. It seems the editor instantiates tools script on their 65 | # own, probably to add them to its tree. In that case, they won't have 66 | # their dependencies injected, so we're not doing anything. 67 | if editor_interface == null: 68 | return 69 | 70 | _apply_custom_header_color() 71 | _set_button_icons() 72 | _connect_signals() 73 | 74 | show_watched_folder(false) 75 | 76 | # ############################################################################ # 77 | # Signals 78 | # ############################################################################ # 79 | 80 | func _configuration_entered(new_text): 81 | _configuration_focus_exited() 82 | 83 | 84 | func _configuration_focus_exited(): 85 | emit_signal("configuration_changed", self) 86 | 87 | 88 | func _remove_button_pressed(): 89 | emit_signal("remove_button_pressed", self) 90 | 91 | 92 | func _build_button_pressed(): 93 | emit_signal("build_button_pressed", self) 94 | 95 | 96 | func _source_file_button_pressed(): 97 | emit_signal("source_file_button_pressed", self) 98 | 99 | 100 | func _target_file_button_pressed(): 101 | emit_signal("target_file_button_pressed", self) 102 | 103 | 104 | func _watched_folder_button_pressed(): 105 | emit_signal("watched_folder_button_pressed", self) 106 | 107 | # ############################################################################ # 108 | # Public Methods 109 | # ############################################################################ # 110 | 111 | func show_watched_folder(show: bool): 112 | watched_folder_label.visible = show 113 | watched_folder_container.visible = show 114 | 115 | func disable_all_buttons(disable: bool): 116 | remove_button.disabled = disable 117 | build_button.disabled = disable 118 | 119 | 120 | # ############################################################################ # 121 | # Private Methods 122 | # ############################################################################ # 123 | 124 | func _apply_custom_header_color(): 125 | var header_color = editor_interface.get_custom_header_color() 126 | if header_color != Color.transparent: 127 | background_color_rect.color = header_color 128 | 129 | 130 | func _set_button_icons(): 131 | var folder_icon = get_icon("Folder", "EditorIcons") 132 | source_file_dialog_button.icon = folder_icon 133 | target_file_dialog_button.icon = folder_icon 134 | watched_folder_dialog_button.icon = folder_icon 135 | 136 | var trash_icon = get_icon("Remove", "EditorIcons") 137 | remove_button.icon = trash_icon 138 | 139 | 140 | func _connect_signals(): 141 | source_file_line_edit.connect("text_entered", self, "_configuration_entered") 142 | source_file_line_edit.connect("focus_exited", self, "_configuration_focus_exited") 143 | 144 | target_file_line_edit.connect("text_entered", self, "_configuration_entered") 145 | target_file_line_edit.connect("focus_exited", self, "_configuration_focus_exited") 146 | 147 | source_file_dialog_button.connect("pressed", self, "_source_file_button_pressed") 148 | target_file_dialog_button.connect("pressed", self, "_target_file_button_pressed") 149 | watched_folder_dialog_button.connect("pressed", self, "_watched_folder_button_pressed") 150 | 151 | remove_button.connect("pressed", self, "_remove_button_pressed") 152 | build_button.connect("pressed", self, "_build_button_pressed") 153 | -------------------------------------------------------------------------------- /addons/inkgd/editor/common/executors/ink_configuration_tester.gd: -------------------------------------------------------------------------------- 1 | # ############################################################################ # 2 | # Copyright © 2019-2022 Frédéric Maquin 3 | # Licensed under the MIT License. 4 | # See LICENSE in the project root for license information. 5 | # ############################################################################ # 6 | 7 | extends InkExternalCommandExecutor 8 | 9 | class_name InkConfigurationTester 10 | 11 | # ############################################################################ # 12 | # Imports 13 | # ############################################################################ # 14 | 15 | var InkExecutionResult = load("res://addons/inkgd/editor/common/executors/structures/ink_execution_result.gd") 16 | 17 | # ############################################################################ # 18 | # Private Properties 19 | # ############################################################################ # 20 | 21 | ## Ink Configuration 22 | var _configuration: InkExecutionConfiguration 23 | 24 | # ############################################################################ # 25 | # Signals 26 | # ############################################################################ # 27 | 28 | ## Emitted when a test completed. Note that this doesn't imply that 29 | ## the test was successful. Check the content of result 30 | ## (InkConfigurationTester.Result) for more information. 31 | signal availability_tested(result) 32 | 33 | # ############################################################################ # 34 | # Overrides 35 | # ############################################################################ # 36 | 37 | func _init(configuration: InkExecutionConfiguration): 38 | _configuration = configuration 39 | 40 | if _configuration.use_threads: 41 | _thread = Thread.new() 42 | 43 | # ############################################################################ # 44 | # Methods 45 | # ############################################################################ # 46 | 47 | ## Test inklecate's availability, based on the configuration provided by this object. 48 | ## If `configuration.use_thread` is set to `false`, this method will return 49 | ## an instance of `InkExecutionResult`, otherwise, it will return `null`. 50 | func test_availability(): 51 | if _configuration.use_threads: 52 | var error = _thread.start(self, "_test_availablity", _configuration, Thread.PRIORITY_HIGH) 53 | if error != OK: 54 | var result = InkExecutionResult.new( 55 | self.identifier, 56 | _configuration.use_threads, 57 | _configuration.user_triggered, 58 | false, 59 | "" 60 | ) 61 | 62 | emit_signal("availability_tested", result) 63 | return true 64 | else: 65 | return _test_availability(_configuration) 66 | 67 | # ############################################################################ # 68 | # Private Helpers 69 | # ############################################################################ # 70 | 71 | ## Test inklecate's availability, based on the configuration provided by this object 72 | ## If `configuration.use_thread` is set to `false`, this method will return 73 | ## an instance of `InkExecutionResult`, otherwise, it will return `null`. 74 | func _test_availability(config: InkExecutionConfiguration): 75 | print("[inkgd] [INFO] Executing test command…") 76 | var return_code = 0 77 | var output = [] 78 | 79 | var start_time = OS.get_ticks_msec() 80 | 81 | if config.use_mono: 82 | var args = [config.inklecate_path] 83 | return_code = OS.execute(config.mono_path, args, true, output, true) 84 | 85 | else: 86 | return_code = OS.execute(config.inklecate_path, [], true, output, true) 87 | 88 | var end_time = OS.get_ticks_msec() 89 | 90 | print("[inkgd] [INFO] Command executed in %dms." % (end_time - start_time)) 91 | 92 | var string_output = PoolStringArray(output) 93 | if _configuration.use_threads: 94 | call_deferred("_handle_test_result", config, return_code, string_output) 95 | return null 96 | else: 97 | return _process_test_result(config, return_code, string_output) 98 | 99 | 100 | ## Handles the test result when exectuted in a different thread. 101 | ## 102 | ## This method should always be executed on the main thread. 103 | func _handle_test_result(config: InkExecutionConfiguration, return_code: int, output: Array): 104 | _thread.wait_to_finish() 105 | 106 | var result = _process_test_result(config, return_code, output) 107 | emit_signal("availability_tested", result) 108 | 109 | 110 | ## Process the compilation results turning them into an instance of `Result`. 111 | ## 112 | ## This method will also print to the editor's output panel. 113 | func _process_test_result( 114 | config: InkExecutionConfiguration, 115 | return_code: int, 116 | output: PoolStringArray 117 | ) -> InkExecutionResult: 118 | var success: bool = (return_code == 0 || _contains_inklecate_output_prefix(output)) 119 | var output_text: String = output.join("\n").replace(BOM, "").strip_edges() 120 | 121 | if success: 122 | if !output_text.empty(): 123 | print("[inkgd] [INFO] inklecate was found and executed:") 124 | print(output_text) 125 | else: 126 | print("[inkgd] [INFO] inklecate was found and executed.") 127 | else: 128 | printerr("[inkgd] [ERROR] Something went wrong while testing inklecate's setup.") 129 | printerr(output_text) 130 | 131 | return InkExecutionResult.new( 132 | self.identifier, 133 | config.use_threads, 134 | config.user_triggered, 135 | success, 136 | output_text 137 | ) 138 | 139 | 140 | ## Guess whether the provided `output_array` looks like the usage inklecate 141 | ## outputs when run with no parameters. 142 | func _contains_inklecate_output_prefix(output_array: PoolStringArray): 143 | # No valid output -> it's not inklecate. 144 | if output_array.size() == 0: return false 145 | 146 | # The first line of the output is cleaned up by removing the BOM and 147 | # any sort of whitespace/unprintable character. 148 | var cleaned_line = output_array[0].replace(BOM, "").strip_edges() 149 | 150 | # If the first line starts with the correct substring, it's likely 151 | # to be inklecate! 152 | return cleaned_line.find("Usage: inklecate2") == 0 153 | -------------------------------------------------------------------------------- /addons/inkgd/mono/InkBridger.cs: -------------------------------------------------------------------------------- 1 | // /////////////////////////////////////////////////////////////////////////// / 2 | // Copyright © 2019-2022 Frédéric Maquin 3 | // Licensed under the MIT License. 4 | // See LICENSE in the project root for license information. 5 | // /////////////////////////////////////////////////////////////////////////// / 6 | 7 | using Godot; 8 | using System; 9 | using System.Linq; 10 | using System.Collections.Generic; 11 | using System.ComponentModel; 12 | 13 | public class InkBridger : Node 14 | { 15 | #region Imports 16 | private readonly GDScript InkPath = 17 | (GDScript) ResourceLoader.Load("res://addons/inkgd/runtime/ink_path.gd"); 18 | 19 | private readonly GDScript InkList = 20 | (GDScript) ResourceLoader.Load("res://addons/inkgd/runtime/lists/ink_list.gd"); 21 | 22 | private readonly GDScript InkListDefinition = 23 | (GDScript) ResourceLoader.Load("res://addons/inkgd/runtime/lists/list_definition.gd"); 24 | 25 | private readonly GDScript InkListItem = 26 | (GDScript) ResourceLoader.Load("res://addons/inkgd/runtime/lists/structs/ink_list_item.gd"); 27 | 28 | private readonly GDScript InkFunctionResult = 29 | (GDScript) ResourceLoader.Load("res://addons/inkgd/runtime/extra/function_result.gd"); 30 | #endregion 31 | 32 | #region Methods | Helpers 33 | public bool IsInkObjectOfType(Godot.Object inkObject, string name) 34 | { 35 | return inkObject.HasMethod("is_class") && (bool)inkObject.Call("is_class", new object[] { name }); 36 | } 37 | 38 | public Godot.Object MakeFunctionResult(string textOutput, object returnValue) 39 | { 40 | var parameters = new object[] { textOutput ?? "", returnValue }; 41 | return (Godot.Object) InkFunctionResult.New(parameters); 42 | } 43 | #endregion 44 | 45 | #region Methods | Conversion -> (GDScript -> C#) 46 | public Godot.Object MakeGDInkPath(Ink.Runtime.Path path) { 47 | var inkPath = (Godot.Object) InkPath.New(); 48 | inkPath.Call("_init_with_components_string", path.componentsString); 49 | return inkPath; 50 | } 51 | 52 | public Godot.Object MakeGDInkList(Ink.Runtime.InkList list) 53 | { 54 | var inkListBase = new Godot.Collections.Dictionary(); 55 | 56 | foreach(KeyValuePair kv in list) { 57 | inkListBase.Add(MakeGDInkListItem(kv.Key).Call("serialized") as string, kv.Value); 58 | } 59 | 60 | object[] inkListParams = new object[] { 61 | inkListBase, 62 | list.originNames.ToArray(), 63 | MakeGDInkListOrigins(list.origins) 64 | }; 65 | 66 | var inkList = (Godot.Object) InkList.New(); 67 | inkList.Call("_init_from_csharp", inkListParams); 68 | 69 | return inkList; 70 | } 71 | 72 | public Ink.Runtime.Path MakeSharpInkPath(Godot.Object path) { 73 | if (!IsInkObjectOfType(path, "InkPath")) 74 | { 75 | throw new ArgumentException("Expected a 'Godot.Object' of class 'InkPath'"); 76 | } 77 | 78 | return new Ink.Runtime.Path((string)path.Get("components_string")); 79 | } 80 | #endregion 81 | 82 | #region Methods | Conversion (GDScript -> C#) 83 | public Ink.Runtime.InkList MakeSharpInkList(Godot.Object list, Ink.Runtime.Story story) 84 | { 85 | if (!IsInkObjectOfType(list, "InkList")) 86 | { 87 | throw new ArgumentException("Expected a 'Godot.Object' of class 'InkList'"); 88 | } 89 | 90 | var underlyingDictionary = new Godot.Collections.Dictionary( 91 | (Godot.Collections.Dictionary)list.Get("_dictionary")); 92 | 93 | var originNames = new Godot.Collections.Array( 94 | (Godot.Collections.Array)list.Get("origin_names")); 95 | 96 | var inkList = new Ink.Runtime.InkList(); 97 | inkList.origins = new List(); 98 | 99 | inkList.SetInitialOriginNames(originNames.ToList()); 100 | 101 | foreach(string originName in originNames) 102 | { 103 | if (story.listDefinitions.TryListGetDefinition (originName, out Ink.Runtime.ListDefinition definition)) 104 | { 105 | if (!inkList.origins.Contains(definition)) { 106 | inkList.origins.Add(definition); 107 | } 108 | } 109 | else 110 | { 111 | throw new Exception ( 112 | $"InkList origin could not be found in story when reconstructing list: {originName}" 113 | ); 114 | } 115 | } 116 | 117 | foreach(KeyValuePair kv in underlyingDictionary) 118 | { 119 | inkList[MakeSharpInkListItem(kv.Key)] = kv.Value; 120 | } 121 | 122 | return inkList; 123 | } 124 | #endregion 125 | 126 | #region Private Methods | Conversion (C# -> GDScript) 127 | private Godot.Collections.Array MakeGDInkListOrigins( 128 | List listDefinitions) 129 | { 130 | var inkListDefinitions = new Godot.Collections.Array(); 131 | 132 | foreach(Ink.Runtime.ListDefinition listDefinition in listDefinitions) { 133 | var inkListDefinition = MakeGDListDefinition(listDefinition); 134 | inkListDefinitions.Add(inkListDefinition); 135 | } 136 | 137 | return inkListDefinitions; 138 | } 139 | 140 | private Godot.Object MakeGDListDefinition(Ink.Runtime.ListDefinition listDefinition) 141 | { 142 | var items = new Godot.Collections.Dictionary(); 143 | 144 | foreach(KeyValuePair kv in listDefinition.items) { 145 | var inkListItem = MakeGDInkListItem(kv.Key); 146 | items.Add(inkListItem, kv.Value); 147 | } 148 | 149 | var definitionParams = new object[] { listDefinition.name, items }; 150 | var inkListDefinition = (Godot.Object) InkListDefinition.New(definitionParams); 151 | 152 | return inkListDefinition; 153 | } 154 | 155 | private Godot.Object MakeGDInkListItem(Ink.Runtime.InkListItem listItem) 156 | { 157 | object[] itemParams = new object[] { listItem.fullName }; 158 | 159 | var inkListItem = (Godot.Object) InkListItem.New(); 160 | inkListItem.Call("_init_with_full_name", itemParams); 161 | 162 | return inkListItem; 163 | } 164 | #endregion 165 | 166 | #region Private Methods | Conversion (GDScript -> C#) 167 | private Ink.Runtime.InkListItem MakeSharpInkListItem(string listItemKey) 168 | { 169 | 170 | var listItem = (Godot.Object) InkListItem.Call("from_serialized_key", new object[] { listItemKey }); 171 | 172 | if (!IsInkObjectOfType(listItem, "InkListItem")) { 173 | throw new ArgumentException("Expected a 'Godot.Object' of class 'InkListItem'"); 174 | } 175 | 176 | return new Ink.Runtime.InkListItem( 177 | listItem.Get("origin_name") as string, 178 | listItem.Get("item_name") as string 179 | ); 180 | } 181 | #endregion 182 | } 183 | --------------------------------------------------------------------------------