diff --git a/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_var_with_data_used.png b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_var_with_data_used.png new file mode 100644 index 0000000000..38aded2f79 Binary files /dev/null and b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_var_with_data_used.png differ diff --git a/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_variable_with_data.png b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_variable_with_data.png new file mode 100644 index 0000000000..ab0ecb9990 Binary files /dev/null and b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/example_variable_with_data.png differ diff --git a/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_duration.png b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_duration.png new file mode 100644 index 0000000000..208196099d Binary files /dev/null and b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_duration.png differ diff --git a/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_string.png b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_string.png new file mode 100644 index 0000000000..41ad5cd4e2 Binary files /dev/null and b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_string.png differ diff --git a/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_used.png b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_used.png new file mode 100644 index 0000000000..d52c449b28 Binary files /dev/null and b/documentation/docs/develop/02-extensions/04-entries/static/assets/variable/generic_used.png differ diff --git a/documentation/docs/develop/02-extensions/04-entries/static/variable.mdx b/documentation/docs/develop/02-extensions/04-entries/static/variable.mdx new file mode 100644 index 0000000000..6f05ac045f --- /dev/null +++ b/documentation/docs/develop/02-extensions/04-entries/static/variable.mdx @@ -0,0 +1,85 @@ +import CodeSnippet from "@site/src/components/CodeSnippet"; +import Figure from "@site/src/components/Figure"; + +# VariableEntry + +The `VariableEntry` is designed to make your entries more dynamic by allowing fields to be computed at runtime. Instead of hardcoding values in your entries, you can use variables that adapt based on the current game state, player context, or other conditions. + +## Understanding Variables + +When creating entries in Typewriter, you often need fields that shouldn't be static. For example, you might want a text that changes based on a player's progress, or a location that depends on where certain events happened in the game. This is where `VariableEntry` comes in - it acts as a dynamic value provider that can return different values based on the current context. + +### Basic Implementation + +Let's start with a simple variable entry: + + + +Every variable entry receives a `VarContext` when it needs to provide its value. This context contains: +- The current player (`context.player`) +- The expected return type (`context.klass`) +- Any associated data for this specific usage (`context.data`) + +### Adding Variable Data + +Variables become more powerful when they can be configured differently at each usage site. This is achieved through variable data: + + + +The relationship between entry fields and variable data is important to understand: + +1. Entry Fields (like `someString`): + - Defined when creating the variable entry + - Remain the same for all uses of this variable entry instance + - Used for configuration that should be consistent + +2. Variable Data Fields (like `otherInfo`): + - Can be different every time the variable is used + - Provide context-specific configuration + - Allow the same variable entry to behave differently in different situations + +
+
+
+
+ +### Working with Generic Types + +Sometimes you need variables that can work with different types of values. The generic system in `VariableEntry` makes this possible: + + + +When working with generics, remember: +- A single instance of a variable entry will always return the same type +- Different instances of the same variable entry can work with different types +- When your variable data uses a generic type, the variable entry must have at least one field using that same generic type +- This ensures that the generic type information flows correctly from the usage site through the data to the entry + +
+
+
+
+
+ +### Type Constraints + +You can restrict which entry fields your variable can be used with: + + + +The `@GenericConstraint` annotation specifies which types of fields this variable can replace. In the example above, this variable can only be used for `String` or `Int` fields in other entries. This ensures that variables are only used in appropriate contexts within your content. + + +## Variable Entry Usage + +Now that you understand the basics of `VariableEntry`, let's see how we can mark fields to allow for dynamic values. + + + +In this example, we have a variable entry that has two fields: `someString` and `someInt`. +The `Var` type is used to represent a variable that can be used in different contexts. + +:::caution +**Not all fields should be marked as variables.** Only fields where it would make sense to have a dynamic value should be marked as a variable. +::: + diff --git a/documentation/package-lock.json b/documentation/package-lock.json index 43a0858b6d..6fca2106d0 100644 --- a/documentation/package-lock.json +++ b/documentation/package-lock.json @@ -8,7 +8,7 @@ "name": "documentation", "version": "0.0.0", "dependencies": { - "@dagrejs/dagre": "^1.1.4", + "@dagrejs/dagre": "^1.1.3", "@docusaurus/core": "^3.6.1", "@docusaurus/faster": "^3.6.1", "@docusaurus/preset-classic": "^3.6.1", diff --git a/documentation/plugins/code-snippets/snippets.json b/documentation/plugins/code-snippets/snippets.json index 199e4f9a60..78df2752d3 100644 --- a/documentation/plugins/code-snippets/snippets.json +++ b/documentation/plugins/code-snippets/snippets.json @@ -69,7 +69,27 @@ }, "speaker_entry": { "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleSpeakerEntry.kt", - "content": "@Entry(\"example_speaker\", \"An example speaker entry.\", Colors.BLUE, \"ic:round-spatial-audio-off\")\nclass ExampleSpeakerEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val displayName: String = \"\",\n override val sound: Sound = Sound.EMPTY,\n) : SpeakerEntry" + "content": "@Entry(\"example_speaker\", \"An example speaker entry.\", Colors.BLUE, \"ic:round-spatial-audio-off\")\nclass ExampleSpeakerEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val displayName: Var = ConstVar(\"\"),\n override val sound: Sound = Sound.EMPTY,\n) : SpeakerEntry" + }, + "variable_entry": { + "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleVariableEntry.kt", + "content": "@Entry(\"example_variable\", \"An example variable entry.\", Colors.GREEN, \"mdi:code-tags\")\nclass ExampleVariableEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n) : VariableEntry {\n override fun get(context: VarContext): T {\n val player = context.player\n val klass = context.klass\n\n TODO(\"Do something with the player and the klass\")\n }\n}" + }, + "variable_entry_with_data": { + "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleVariableEntry.kt", + "content": "@Entry(\"example_variable_with_data\", \"An example variable entry with data.\", Colors.GREEN, \"mdi:code-tags\")\n// Register the variable data associated with this variable.\n@VariableData(ExampleVariableWithData::class)\nclass ExampleVariableWithDataEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n // This data will be the same for all uses of this variable.\n val someString: String = \"\",\n) : VariableEntry {\n override fun get(context: VarContext): T {\n val player = context.player\n val klass = context.klass\n this.someString\n val data = context.getData() ?: throw IllegalStateException(\"Could not find data for ${context.klass}, data: ${context.data}\")\n\n TODO(\"Do something with the player, the klass, and the data\")\n }\n}\n\nclass ExampleVariableWithData(\n // This data can change at the place where the variable is used.\n val otherInfo: Int = 0,\n)" + }, + "generic_variable_entry": { + "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleVariableEntry.kt", + "content": "@Entry(\"example_generic_variable\", \"An example generic variable entry.\", Colors.GREEN, \"mdi:code-tags\")\nclass ExampleGenericVariableEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n // We determine how to parse this during runtime.\n val generic: Generic = Generic.Empty,\n) : VariableEntry {\n override fun get(context: VarContext): T {\n val player = context.player\n val klass = context.klass\n\n // Parse the generic data to the correct type.\n val data = generic.get(klass)\n\n TODO(\"Do something with the player, the klass, and the generic\")\n }\n}\n\nclass ExampleGenericVariableData(\n // Generic data will always be the same as the generic type in the variable.\n val otherGeneric: Generic,\n)" + }, + "constraint_variable_entry": { + "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleVariableEntry.kt", + "content": "@Entry(\"example_constraint_variable\", \"An example constraint variable entry.\", Colors.GREEN, \"mdi:code-tags\")\n@GenericConstraint(String::class)\n@GenericConstraint(Int::class)\nclass ExampleConstraintVariableEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n // We determine how to parse this during runtime.\n val generic: Generic = Generic.Empty,\n) : VariableEntry {\n override fun get(context: VarContext): T {\n val player = context.player\n // This can only be a String or an Int.\n val klass = context.klass\n\n // Parse the generic data to the correct type.\n val data = generic.get(klass)\n\n TODO(\"Do something with the player, the klass, and the generic\")\n }\n}" + }, + "variable_usage": { + "file": "src/main/kotlin/com/typewritermc/example/entries/static/ExampleVariableEntry.kt", + "content": "@Entry(\"example_action_using_variable\", \"An example action that uses a variable.\", Colors.RED, \"material-symbols:touch-app-rounded\")\nclass ExampleActionUsingVariableEntry(\n override val id: String = \"\",\n override val name: String = \"\",\n override val triggers: List> = emptyList(),\n override val criteria: List = emptyList(),\n override val modifiers: List = emptyList(),\n val someString: Var = ConstVar(\"\"),\n val someInt: Var = ConstVar(0),\n) : ActionEntry {\n override fun execute(player: Player) {\n val someString = someString.get(player)\n val someInt = someInt.get(player)\n\n // Do something with the variables\n }\n}" }, "action_entry": { "file": "src/main/kotlin/com/typewritermc/example/entries/trigger/ExampleActionEntry.kt", diff --git a/documentation/src/components/Figure/index.tsx b/documentation/src/components/Figure/index.tsx index c2cf2d2870..30a41ebc83 100644 --- a/documentation/src/components/Figure/index.tsx +++ b/documentation/src/components/Figure/index.tsx @@ -6,9 +6,9 @@ interface FigureProps extends Props { export default function Figure({ title, ...props }: FigureProps) { return ( -
+
{title}
-
+ ); }