Skip to content

Commit

Permalink
Update development documentation for v0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
gabber235 committed Dec 18, 2024
1 parent 396be20 commit 9bafb71
Show file tree
Hide file tree
Showing 34 changed files with 512 additions and 196 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ It is important to stress that the entry must be immutable.
## Usage
<CodeSnippet tag="action_entry" json={require("../../../snippets.json")} />

Typewriter will automatically trigger the next entries in the chain after the `execute` method is called.
If you want to call the next entries in the chain manually, you can should the [CustomTriggeringActionEntry](./custom_triggering_action.mdx).
Typewriter will automatically trigger the next entries in the chain after the `execute` method is called, and apply all the modifiers.

Sometimes you want to have a little more control over when the next triggers are triggered, or what triggers are triggered.

<CodeSnippet tag="action_entry_manual" json={require("../../../snippets.json")} />

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ This is automatically handled by Typewriter.
To define the messenger that will be used to display the dialogue to the player, you must create a class that implements the `DialogueMessenger` interface.
<CodeSnippet tag="dialogue_messenger" json={require("../../../snippets.json")} />

### Multiple Messengers
The `DialogueMessenger` has a `MessengerFilter` that is used to determine if the messenger should be used to display the dialogue to the player. When having multiple `MessageFilter`'s make sure that they are deterministic. So if you have some condition, such as if they are bedrock players. One message check that the player is a bedrock player and the other filter check that they are not.

### Lifecycle
The `state` of the messenger determines what happens to the messenger.
- `MessengerState.FINISHED` - The dialogue is finished and the next dialogue in the chain will be triggered.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ To listen to an event, you must create a function that is annotated with `@Entry
The great thing about kotlin, is that this can be done in the same file as the entry.
<CodeSnippet tag="event_entry_listener" json={require("../../../snippets.json")} />

:::warning Public Function
If the function is not scoped to be public, it will not be registered as a listener.
:::

The function will automatically be registered as a listener for the event by Typewriter and be called when the Bukkit event is trigger.
An optional `Query` parameter can be added to easily fetch all the different event entries.

## Entry Context Variables
Sometimes you want to pass some information to the context so that subsequent entries can use it.

<CodeSnippet tag="event_entry_with_context_keys" json={require("../../../snippets.json")} />
34 changes: 9 additions & 25 deletions documentation/docs/develop/02-extensions/05-querying.mdx
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
import CodeSnippet from "@site/src/components/CodeSnippet";

# Query Entries

Sometimes you need to find an entry by any of it's fields or by type. This can be done with the `Query` class.

If you need to find all entries of a specific type:
```kotlin
val entries = Query.find<MyEntry>()
```

Sometimes you need it by a specific criteria:
```kotlin
val entries = Query.findWhere<MyEntry> {
it.someField == "some value"
}
```

You can also find a single entry:
```kotlin
val entry = Query.findFirstWhere<MyEntry> {
it.someField == "some value"
}
```
<CodeSnippet tag="query_multiple" json={require("../snippets.json")} />

Sometimes you need it by a specific filter:
<CodeSnippet tag="query_multiple_with_filter" json={require("../snippets.json")} />


Sometimes you need to find an entry by it's id:
```kotlin
val entry = Query.findById<MyEntry>(id)
```
<CodeSnippet tag="query_by_id" json={require("../snippets.json")} />

Other times you need to find entries by their page:
```kotlin
val entries = Query.findWhereFromPage<MyEntry>(pageId) {
it.someField == "some value"
}
```
<CodeSnippet tag="query_from_page" json={require("../snippets.json")} />

67 changes: 18 additions & 49 deletions documentation/docs/develop/02-extensions/06-triggering.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import CodeSnippet from "@site/src/components/CodeSnippet";

# Triggering Entries

There are easy ways to trigger all the next entries in a `TriggerEntry`.
Expand All @@ -9,69 +11,36 @@ If criteria are not met, the entries are not triggered.
:::

If you have a single `TriggerEntry`:
```kotlin
val triggerEntry: TriggerEntry = ...
val player: Player = ...
triggerEntry triggerAllFor player
```

If you have list of `TriggerEntry`:
```kotlin
val triggerEntries: List<TriggerEntry> = ...
val player: Player = ...
triggerEntries triggerAllFor player
```

If you have a list of id's of `TriggerEntry`:
```kotlin
val triggerEntryIds: List<String> = ...
val player: Player = ...
triggerEntryIds triggerEntriesFor player
```

Sometimes you don't want to trigger the entries when the player is in a dialogue.
<CodeSnippet tag="trigger_without_context" json={require("../snippets.json")} />

Sometimes you don't want to trigger the entries when the player is in a dialogue.
For example, when the player is in a dialogue with a NPC, you don't want to trigger the first entry of the NPC again.
You expect when the player clicks on the NPC again, the next dialogue is triggered.
To facilitate this, you can use the `startDialogueWithOrNextDialogue` function.

```kotlin
val triggerEntries: List<TriggerEntry> = ...
val player: Player = ...
triggerEntries startDialogueWithOrNextDialogue player
```
<CodeSnippet tag="start_dialogue_with_or_trigger" json={require("../snippets.json")} />

Or if you want to trigger something completely different when the player is in a dialogue:
```kotlin
val triggerEntries: List<TriggerEntry> = ...
val player: Player = ...
val customTrigger: EventTrigger = ...
triggerEntries.startDialogueWithOrTrigger(player, customTrigger)
```
Previously, we triggered entries with a new fresh `context`, but sometimes we already have a context.
Then you need to make sure that you pass on the context to the trigger.

<CodeSnippet tag="trigger_with_context" json={require("../snippets.json")} />

## Custom triggers
Typewriter triggers based on the `EventTrigger` interface.
So all the entries that are triggered are wrapped in a `EntryTrigger`.

There are some triggers that are defined in Typewriter.
The two are `SystemTrigger` and `CinematicStartTrigger`.
### System Triggers

For some interactions there are custom triggers that are defined in Typewriter.
These triggers allow you to do special things with the interaction.

### SystemTrigger
<CodeSnippet tag="interaction_triggers" json={require("../snippets.json")} />

`SystemTrigger`'s can be used to indicate to either the `DialogueSequence` or the `CinematicSequence` that something needs to happen.
#### Dialogue Interaction

- `SystemTrigger.DIALOGUE_NEXT` indicates that the next dialogue should be triggered.
- `SystemTrigger.DIALOGUE_END` indicates that the dialogue should end.
- `SystemTrigger.CINEMATIC_END` indicates that the cinematic should end.

### CinematicStartTrigger
<CodeSnippet tag="dialogue_triggers" json={require("../snippets.json")} />

`CinematicStartTrigger`'s can be used to indicate to the `CinematicSequence` that a cinematic should start.
#### Temporal Interaction

It has several properties that can be set:
- `pageId: String` is the id of the cinematic page that should be shown. This is required.
- `triggers: List<String>` is a list of trigger id's that should be triggered when the cinematic is finished. This is optional.
- `override: Boolean` indicates if the cinematic should override the current cinematic. This is optional and defaults to `false`.
- `simulate: Boolean` is used to run a cinematic for recording purposes. When this is enable it disables some entries from running. This is optional and defaults to `false`.
- `ignoreEntries: List<String>` is a list of entry id's that should not be triggered. This is optional.
- `minEndTime: Optional<Int>` is the minimum amount of frames the cinematic should run. If the cinematic is shorter than this, it will be extended. This is optional.
<CodeSnippet tag="temporal_triggers" json={require("../snippets.json")} />
168 changes: 166 additions & 2 deletions documentation/docs/develop/02-extensions/07-api-changes/0.8.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ Now the engine version is not specified in a seperate `engine` block, but in the
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
kotlin("jvm") version "2.0.21"
id("com.typewritermc.module-plugin") version "<module plugin version>"
// highlight-red
id("com.typewritermc.module-plugin") version "1.0.1"
// highlight-green
id("com.typewritermc.module-plugin") version "1.1.0"
}
// Replace with your own information
group = "me.yourusername"
Expand Down Expand Up @@ -57,7 +60,10 @@ Now the engine version is not specified in a seperate `engine` block, but in the
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
kotlin("jvm") version "2.0.21"
id("com.typewritermc.module-plugin") version "<module plugin version>"
// highlight-red
id("com.typewritermc.module-plugin") version "1.0.1"
// highlight-green
id("com.typewritermc.module-plugin") version "1.1.0"
}
// Replace with your own information
group = "me.yourusername"
Expand Down Expand Up @@ -93,3 +99,161 @@ Now the engine version is not specified in a seperate `engine` block, but in the
```
</TabItem>
</Tabs>

## `Initializable` Changes

The way to register a `Initializable` has changed.

```kotlin showLineNumbers
// highlight-red
@Initializer
// highlight-green
@Singleton
object ExampleInitializer : Initializable {
override suspend fun initialize() {
// Do something when the extension is initialized
}

override suspend fun shutdown() {
// Do something when the extension is shutdown
}
}

```

## Triggering Entries Changes

Since Typewriter now has `InteractionContext`, this needs to be passed around when triggering entries.
As a lot changed, have a look at the [Triggering Entries](../triggering) page for more information.

```kotlin showLineNumbers
val entries: List<TriggerableEntry> = ...

// highlight-red
entries triggerAllFor player
// highlight-green
entries.triggerAllFor(player, context())
```

## ActionEntry Changes

As the `ActionEntry` now needs a `InteractionContext`, the `execute` function signature has changed.

```kotlin showLineNumbers
@Entry("example_action", "An example action entry.", Colors.RED, "material-symbols:touch-app-rounded")
class ExampleActionEntry(
override val id: String = "",
override val name: String = "",
override val criteria: List<Criteria> = emptyList(),
override val modifiers: List<Modifier> = emptyList(),
override val triggers: List<Ref<TriggerableEntry>> = emptyList(),
) : ActionEntry {
// highlight-red-start
override fun execute(player: Player) {
super.execute(player) // This will apply all the modifiers.
// highlight-red-end
// highlight-green-start
override fun ActionTrigger.execute() {
// Will now automatically apply all the modifiers.
// Do something with the player
}
}

## Removal of `CustomTriggeringActionEntry`

As the action entry now can specify itself how Typewriter should handle the triggering:

```kotlin showLineNumbers
@Entry("example_action", "An example action entry.", Colors.RED, "material-symbols:touch-app-rounded")
class ExampleCustomTriggeringActionEntry(
override val id: String = "",
override val name: String = "",
override val criteria: List<Criteria> = emptyList(),
override val modifiers: List<Modifier> = emptyList(),
// highlight-red-start
@SerializedName("triggers")
override val customTriggers: List<Ref<TriggerableEntry>> = emptyList(),
) : CustomTriggeringActionEntry {
override fun execute(player: Player) {
super.execute(player) // This will apply the modifiers.
// Do something with the player
player.triggerCustomTriggers() // Can be called later to trigger the next entries.
// highlight-red-end
// highlight-green-start
override val triggers: List<Ref<TriggerableEntry>> = emptyList(),
) : ActionEntry {
override fun ActionTrigger.execute() {
// This disables Typewriter's automatic triggering of the next entries,
// and disables the automatic apply of the modifiers.
disableAutomaticTriggering()

// Now you can manually trigger the next entries.
triggerManually()

// Or if you want to specify which triggers to trigger, you can do so.
triggers.filterIndexed { index, _ -> index % 2 == 0 }.triggerFor(player)

// You can also manually apply the modifiers.
applyModifiers()
// highlight-green-end
}
}
```

## Dialogue Messenger Changes

As it was the only place in Typewriter where you had to register something with an annotation, we changed it to allow the DialogueEntry to specify the messengers that it uses.
Additionally, we now need to forward the `context` parameter to the messengers.

```kotlin showLineNumbers
@Entry("example_dialogue", "An example dialogue entry.", Colors.BLUE, "material-symbols:chat-rounded")
class ExampleDialogueEntry(
override val id: String = "",
override val name: String = "",
override val criteria: List<Criteria> = emptyList(),
override val modifiers: List<Modifier> = emptyList(),
override val triggers: List<Ref<TriggerableEntry>> = emptyList(),
override val speaker: Ref<SpeakerEntry> = emptyRef(),
@MultiLine
@Placeholder
@Colored
@Help("The text to display to the player.")
val text: String = "",
) : DialogueEntry {
// highlight-green-start
// May return null to skip the dialogue
override fun messenger(player: Player, context: InteractionContext): DialogueMessenger<*>? {
// You can use if statements to return a different messenger depending on different conditions
return ExampleDialogueDialogueMessenger(player, context, this)
}
// highlight-green-end
}

// highlight-red-start
@Messenger(ExampleDialogueEntry::class)
class ExampleDialogueDialogueMessenger(player: Player, entry: ExampleDialogueEntry) :
DialogueMessenger<ExampleDialogueEntry>(player, entry) {

companion object : MessengerFilter {
override fun filter(player: Player, entry: DialogueEntry): Boolean = true
}
// highlight-red-end
// highlight-green-start
class ExampleDialogueDialogueMessenger(player: Player, context: InteractionContext, entry: ExampleDialogueEntry) :
DialogueMessenger<ExampleDialogueEntry>(player, context, entry) {
// highlight-green-end


// Called every game tick (20 times per second).
// The cycle is a parameter that is incremented every tick, starting at 0.
override fun tick(context: TickContext) {
super.tick(context)
if (state != MessengerState.RUNNING) return

player.sendMessage("${entry.speakerDisplayName}: ${entry.text}".parsePlaceholders(player).asMini())

// When we want the dialogue to end, we can set the state to FINISHED.
state = MessengerState.FINISHED
}
}
```
Loading

0 comments on commit 9bafb71

Please sign in to comment.