Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
paulloz committed Sep 21, 2019
2 parents 6842b55 + 59e77af commit 05f82be
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 130 deletions.
85 changes: 39 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,31 @@
# godot-ink

**This project is heavily under construction as C# support in Godot is a WIP.**
An [ink](https://github.com/inkle/ink) integration for [Godot Engine](https://github.com/godotengine/godot).

[Ink](https://github.com/inkle/ink) integration for [Godot Engine](https://github.com/godotengine/godot).
As C# supprt in Godot is still in its early stages, you might need to manually add the addon files to your `.csproj`.

### Currently supported features:
* Running an Ink story and branching with choice indexes
* Saving and loading Ink state
* Tags
* Knot/Stitch jumping
* Getting/Setting Ink variables (InkLists aren't supported yet)
* Observing Ink variables (Inklists aren't supported yet)
* External function bindings
* Read/Visit counts

### TODO:
* Getting/Setting/Observing InkLists
* On the fly Ink to JSON compilation

## Troubleshooting

If you're having trouble enabling the editor plugin, it's probably because the `.cs` files aren't compiling with your project. You can solve the issue by adding this `ItemGroup` to your `.csproj` file.

```xml
<ItemGroup>
<Compile Include="addons\paulloz.ink\PaullozDotInk.cs" />
<Compile Include="addons\paulloz.ink\InkDock.cs" />
<Compile Include="addons\paulloz.ink\Story.cs" />
<Reference Include="Ink">
<HintPath>$(ProjectDir)/ink-engine-runtime.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
```

Depending on the version of Godot you're using, you might still have issues with the editor plugin.
Do not worry, you don't actually need to enable it to use **godot-ink**. If you don't want to bother with extensive troubleshooting, all you have to do is attach `addons/paulloz.ink/Story.cs` to a node (or use it as a singleton). This node will become the `Story` node for the rest of this documentation.

### Game export
## How to use

As your `.json` files aren't Godot resources, you'll need to manually tell the engine to include them in the exported package. You can read more about that in the [documentation](https://godot.readthedocs.io/en/latest/getting_started/workflow/export/exporting_projects.html?highlight=export#export-mode).
When the plugin is properly loaded, you should be able to use the new ink panel to inspect your story.

## How to use
![](inspector_screenshot.png)

You'll need to put `ink-engine-runtime.dll` at the root of your Godot project.
You'll also see a new `ink` section in your project settings. If you want to be able to compile your .ink files on the fly you can input the path to the inklecate binary here.
The last thing you'll need to do in order to get going is to put `ink-engine-runtime.dll` at the root of your Godot project.

Everything revolves around the `Story` node.
---

Everything is handled in a `Story` node.
If nothing is specified, the **C#** usage is the same as the **GDScript** one.

### Loading the story

First you should navigate to your `.json` or `.ink` file and import it as an `Ink story` in Godot. To do that, select the file in Godot, go to `Import`, select `Ink story` under `Import As:` and click `ReImport`.

![](import_screenshot.png)

To load your story, you can:

* Point the `InkFilePath` exported variable to the location of your JSON Ink file and check the `AutoLoadStory` checkbox in the inspector.
* Point the `InkFilePath` exported variable to the location of your JSON Ink file (in the inspector or via a script) and call `story.LoadStory()`.
* Call `story.LoadStory(path)` with path pointing to the location of your JSON Ink file.
* Point the `InkFile` exported variable to your `.json`/`.ink` file and check the `AutoLoadStory` checkbox in the inspector.
* Point the `InkFile` exported variable to your `.json`/`.ink` file (in the inspector or via a script) and call `story.LoadStory()`.

### Running the story and making choices

Expand Down Expand Up @@ -111,7 +80,8 @@ You get and set the json state by calling `.GetState()` and `.SetState(String)`.
story.SetState(story.GetState())
```

Alternatively you can save and load directly from disk (either by passing a path or a file as argument) with `.LoadStateFromDisk` and `.SaveStateOnDisk`.
Alternatively you can save and load directly from disk (either by passing a path or a file as argument) with `.LoadStateFromDisk` and `.SaveStateOnDisk`.
When using a path, the default behaviour is to use the `user://` folder. You can bypass this by passing a full path to the functions (e.g. `res://my_dope_save_file.json`).

```GDScript
story.SaveStateOnDisk("save.json")
Expand Down Expand Up @@ -178,6 +148,29 @@ You can know how many times a knot/stitch has been visited with `.VisitCountPath
print(story.VisitCountPathString("mycoolknot.myradstitch"))
```

## Troubleshooting

If you're having trouble enabling the editor plugin, it's probably because the `.cs` files aren't compiling with your project. You can solve the issue by adding this `ItemGroup` to your `.csproj` file.

```xml
<ItemGroup>
<Compile Include="addons\paulloz.ink\PaullozDotInk.cs" />
<Compile Include="addons\paulloz.ink\InkDock.cs" />
<Compile Include="addons\paulloz.ink\Story.cs" />
<Reference Include="Ink">
<HintPath>$(ProjectDir)/ink-engine-runtime.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
```

Depending on the version of Godot you're using, you might still have issues with the editor plugin.
Do not worry, you don't actually need to enable it to use **godot-ink**. If you don't want to bother with extensive troubleshooting, all you have to do is attach `addons/paulloz.ink/Story.cs` to a node (or use it as a singleton). This node will become the `Story` node for the rest of this documentation.

### TODO:
* Getting/Setting/Observing InkLists
* On the fly Ink to JSON compilation (works on Windows, need some tweaking for Linux and Mac OS support)

## License

**godot-ink** is released under MIT license (see the [LICENSE](/LICENSE) file for more information).
57 changes: 44 additions & 13 deletions addons/paulloz.ink/InkDock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@ public class InkDock : Control
private String currentFilePath;

private Node storyNode;
private RichTextLabel storyText;
private VBoxContainer storyText;
private VBoxContainer storyChoices;

private ScrollBar scrollbar;

public override void _Ready()
{
fileSelect = GetNode<OptionButton>("VBoxContainer/GridContainer/OptionButton");
fileSelect = GetNode<OptionButton>("Container/Top/OptionButton");
fileDialog = GetNode<FileDialog>("FileDialog");
fileSelect.Connect("item_selected", this, nameof(onFileSelectItemSelected));
fileDialog.Connect("file_selected", this, nameof(onFileDialogFileSelected));
fileDialog.Connect("popup_hide", this, nameof(onFileDialogHide));

storyNode = GetNode("Story");
storyNode.SetScript(ResourceLoader.Load("res://addons/paulloz.ink/Story.cs") as Script);
storyNode.Connect(nameof(Story.InkContinued), this, nameof(onStoryContinued));
storyNode.Connect(nameof(Story.InkChoices), this, nameof(onStoryChoices));
storyText = GetNode<RichTextLabel>("VBoxContainer/StoryText");
storyChoices = GetNode<VBoxContainer>("VBoxContainer/StoryChoices");
storyNode.SetScript(ResourceLoader.Load("res://addons/paulloz.ink/InkStory.cs") as Script);
storyNode.Connect(nameof(InkStory.InkChoices), this, nameof(onStoryChoices));
storyText = GetNode<VBoxContainer>("Container/Bottom/Scroll/Margin/StoryText");
storyChoices = GetNode<VBoxContainer>("Container/Bottom/StoryChoices");

scrollbar = this.GetNode<ScrollContainer>("Container/Bottom/Scroll").GetVScrollbar();
}

private void resetFileSelectItems()
Expand All @@ -38,7 +41,18 @@ private void resetFileSelectItems()

private void resetStoryContent()
{
storyText.Text = "";
this.removeAllStoryContent();
this.removeAllChoices();
}

private void removeAllStoryContent()
{
foreach (Node n in storyText.GetChildren())
storyText.RemoveChild(n);
}

private void removeAllChoices()
{
foreach (Node n in storyChoices.GetChildren())
storyChoices.RemoveChild(n);
}
Expand Down Expand Up @@ -75,21 +89,37 @@ private void onFileDialogHide()
else
{
fileSelect.Select(2);
storyNode.Set("InkFilePath", currentFilePath.Remove(0, 6));
storyNode.Set("InkFile", ResourceLoader.Load(currentFilePath));
storyNode.Call("LoadStory");
resetStoryContent();
continueStoryMaximally();
}
}

private void continueStoryMaximally()
private async void continueStoryMaximally()
{
while ((bool)storyNode.Get("CanContinue"))
storyNode.Call("Continue");
{
try
{
storyNode.Call("Continue");
onStoryContinued(storyNode.Get("CurrentText") as String, new String[] { });
}
catch (Ink.Runtime.StoryException e)
{
onStoryContinued(e.ToString(), new String[] { });
}
}
await ToSignal(GetTree(), "idle_frame");
this.scrollbar.Value = this.scrollbar.MaxValue;
}

private void onStoryContinued(String text, String[] tags)
{
storyText.Text = (storyText.Text + text).TrimStart(new char[] { ' ', '\n' });
Label newLine = new Label();
newLine.Autowrap = true;
newLine.Text = text.Trim(new char[] { ' ', '\n' });
this.storyText.AddChild(newLine);
}

private void onStoryChoices(String[] choices)
Expand All @@ -107,8 +137,9 @@ private void onStoryChoices(String[] choices)

private void clickChoice(int idx)
{
resetStoryContent();
storyNode.Callv("ChooseChoiceIndex", new Godot.Collections.Array() { idx });
this.removeAllChoices();
this.storyText.AddChild(new HSeparator());
continueStoryMaximally();
}
}
Expand Down
65 changes: 47 additions & 18 deletions addons/paulloz.ink/InkDock.tscn
Original file line number Diff line number Diff line change
@@ -1,52 +1,82 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=4 format=2]

[ext_resource path="res://addons/paulloz.ink/InkDock.cs" type="Script" id=1]
[ext_resource path="res://addons/paulloz.ink/Story.cs" type="Script" id=2]
[ext_resource path="res://addons/paulloz.ink/InkStory.cs" type="Script" id=2]

[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0.137255, 0.160784, 0.184314, 1 )

[node name="Ink" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 0, 200 )
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource( 1 )

[node name="VBoxContainer" type="VBoxContainer" parent="."]
[node name="Container" type="VBoxContainer" parent="."]
anchor_right = 1.0
margin_bottom = 24.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3

[node name="GridContainer" type="GridContainer" parent="VBoxContainer"]
[node name="Top" type="HBoxContainer" parent="Container"]
editor/display_folded = true
margin_right = 1024.0
margin_bottom = 20.0
size_flags_horizontal = 3
size_flags_vertical = 0
columns = 2

[node name="Label" type="Label" parent="VBoxContainer/GridContainer"]
[node name="Label" type="Label" parent="Container/Top"]
margin_top = 3.0
margin_right = 40.0
margin_bottom = 17.0
text = "Story :"

[node name="OptionButton" type="OptionButton" parent="VBoxContainer/GridContainer"]
[node name="OptionButton" type="OptionButton" parent="Container/Top"]
margin_left = 44.0
margin_right = 85.0
margin_bottom = 20.0
items = [ "", null, false, 0, null, "Load", null, false, 1, null ]
selected = 0

[node name="StoryText" type="RichTextLabel" parent="VBoxContainer"]
[node name="Bottom" type="HBoxContainer" parent="Container"]
margin_top = 24.0
margin_right = 1024.0
margin_bottom = 624.0
rect_min_size = Vector2( 100, 600 )
margin_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_colors/default_color = Color( 1, 1, 1, 1 )

[node name="StoryChoices" type="VBoxContainer" parent="VBoxContainer"]
margin_top = 628.0
[node name="Scroll" type="ScrollContainer" parent="Container/Bottom"]
margin_right = 820.0
margin_bottom = 576.0
size_flags_horizontal = 11
size_flags_vertical = 3
custom_styles/bg = SubResource( 1 )
scroll_horizontal_enabled = false

[node name="Margin" type="MarginContainer" parent="Container/Bottom/Scroll"]
margin_right = 820.0
margin_bottom = 576.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_constants/margin_right = 10
custom_constants/margin_top = 10
custom_constants/margin_left = 10
custom_constants/margin_bottom = 10

[node name="StoryText" type="VBoxContainer" parent="Container/Bottom/Scroll/Margin"]
margin_left = 10.0
margin_top = 10.0
margin_right = 810.0
margin_bottom = 566.0
size_flags_horizontal = 11
size_flags_vertical = 3

[node name="StoryChoices" type="VBoxContainer" parent="Container/Bottom"]
margin_left = 824.0
margin_right = 1024.0
margin_bottom = 628.0
margin_bottom = 576.0
rect_min_size = Vector2( 200, 0 )
custom_constants/separation = 10

[node name="FileDialog" type="FileDialog" parent="."]
margin_right = 461.0
Expand All @@ -57,4 +87,3 @@ filters = PoolStringArray( "*.json" )

[node name="Story" type="Node" parent="."]
script = ExtResource( 2 )

Loading

0 comments on commit 05f82be

Please sign in to comment.