# Hytale Modding Documentation
> The number one community resource for modding Hytale, featuring comprehensive guides, detailed documentation, and essential tools to kickstart your modding journey.
--- Quick Start
Source: https://hytalemodding.dev/en/docs/quick-start
Welcome to the Hytale modding documentation! Here you'll find everything you need to know about creating your own mods for Hytale.
## Where to start
If you don't know anything about modding Hytale or even Java, or you would like to brush up on your knowledge, here are some links that will help you with that.
* [Java Basics](./guides/java-basics/00-introduction) - learn the Java syntax and language features you'll need for modding.
* [Learning to learn](./guides/learning-to-learn) - why are we using Java.
* [Setting up your dev environment](./guides/plugin/setting-up-env) - set up your project for development.
* [Build and test](.guide/plugin/build-and-test) - build your project.
* [Logging](./guides/plugin/logging) - find and interpret output from your mod when you run the game.
* [Creating a Block](./guides/plugin/creating-block) - build a simple block to verify your setup works.
If you have experience or you want to try something harder:
* [World Generation](./guides/plugin/world-gen) - add new terrain, biomes, and custom ore spawns.
* [Custom Camera Controls](./guides/plugin/customizing-camera-controls) - change how the player camera behaves during gameplay.
* [Entity Component System (ECS)](./guides/ecs/entity-component-system) - how to use ECS.
* [Prefabs](./guides/prefabs) - build reusable structures and templates for world editing.
* [Publishing your mod](./publishing) - package and share your mod with others.
## Guides
Here are some links to guides that will help you learn how to make mods.
### Video guides
* [HytaleModding's Modding Videos](https://www.youtube.com/@HytaleModding)
* [Kaupenjoe's Modding Videos](https://www.youtube.com/@ModdingByKaupenjoe)
* [TroubleDEV's Modding Videos](https://www.youtube.com/@TroubleDEV)
--- Introduction
Source: https://hytalemodding.dev/en/docs
Hytale is a voxel-based sandbox RPG game by **Hypixel Studios**, as described on the [Hytale website](https://hytale.com/):
> Set out on an adventure built for both creation and play. Hytale blends the freedom
> of a sandbox with the momentum of an RPG: explore a procedurally generated world full
> of dungeons, secrets, and a variety of creatures, then shape it block by block.
Hytale has been built from the ground up with modding and user-generated content in mind.
The goal of **Hytale Modding** is to provide an excellent resource so that
anyone can mod Hytale themselves, regardless of skill level or specialization.
It is important to note that modding Hytale is not restricted to programming plugins only.
Programming, visual scripting, texturing, 3D modelling, modifying assets, etc., are all modding.
In Hytale, anyone can be a modder.
--- 6 - GitHub Sync
Source: https://hytalemodding.dev/en/docs/wiki/6-github
## Overview
If your documentation already lives on GitHub, you can sync it directly with your mod wiki.
Changes pushed to your repository will automatically appear on the site — no manual updates needed.
Once GitHub sync is enabled, the built-in editor will be disabled.
All edits must be made in your repository.
## Setting up
Go to your mod settings and scroll down to **GitHub Repository URL**.
Paste your repository link and save.
If your documentation is inside a subfolder, set the path in **Repository Path**.
If the entire repository is documentation, leave this field empty.
## Repository structure
Your repository should contain `.md` files at the root of your documentation folder:
```
my-repo/
└── docs/
├── intro.md
├── installation.md
└── usage.md
```
Each file becomes a page on your wiki. The filename (without `.md`) is used as the page slug.
## Frontmatter
You can let our software know how you would like to configure and show your pages via the Frontmatter on all Markdown (.md) files. The Frontmatter must be at the very beginning of your files.
Here is a table of available fields:
| Field | Type | Required | Default | Description |
| --------- | ------- | -------- | ------------------------- | ------------------------------------------- |
| title | string | No | Derived from filename | Override the page title. |
| order | integer | No | Position in the file list | Sort among siblings (lower = appears first) |
| published | boolean | No | false | Whether the page is published. |
| draft | boolean | No | false | Whether the page is a draft. |
### Example
```md
---
title: "Introduction"
order: 1
published: true
draft: false
---
# Introduction
```
## Categorys and Hierarchy
If you want to make children pages under a page, you need to create a folder. If you want the parent page to have contents, you can make a file named `index.md` and put the content inside it. The rest of the pages in that folder will be children of the folder.
If you want to create a category and do not want content on the parent page, you can make a `meta.json` file which will let you modify the title of the category, etc.
### Example
```json
{
"title": "My Category",
"published": true
}
```
## Updating documentation
Push changes to your repository as usual — the wiki will update automatically.
```bash
git add .
git commit -m "Update documentation"
git push
```
--- 1 - Account Creation
Source: https://hytalemodding.dev/en/docs/wiki/1-create-account
## 1. Creating an account
Go to the [site](https://wiki.hytalemodding.dev/) and click **Register**.
Already have an account? Just click **Login** instead.
After registering, you'll be taken to the main page. You don't need it right now, but it will come up in the next step.
## 2. Setting up your account
To access your settings, click your profile picture in the top-right corner and select **Settings** —
or follow [this link](https://wiki.hytalemodding.dev/settings/profile) directly.
On the left you'll find four sections:
* [**Profile**](https://wiki.hytalemodding.dev/settings/profile): Basic account information.
* [**Password**](https://wiki.hytalemodding.dev/settings/password): Change your password.
* [**Two-Factor Auth**](https://wiki.hytalemodding.dev/settings/two-factor): Add an extra layer of security to your account.
* [**Appearance**](https://wiki.hytalemodding.dev/settings/appearance): Customize the look of the site.
To update your profile picture, click **Upload Photo** and choose an image. That's all for the basic setup.
--- 3 - Creating Pages
Source: https://hytalemodding.dev/en/docs/wiki/3-creating-pages
## Creating a page
To add a page to your mod, open your mod's management page and click **New Page**.
This will take you to the page editor — the screen you'll spend most of your time on.
### Page settings
At the top you'll find the page configuration fields:
* **Page title** *(Required)*: The name of your page as it will appear in the sidebar and heading.
* **Page type** *(Optional, default: "Page")*: Choose between **Page** (regular content) or **Category** (an empty grouping page with no content of its own).
* **Parent page** *(Optional)*: Nest this page under another — useful for building a hierarchy.
* **Index page** *(Optional)*: Mark this page as the mod's landing page, shown when someone first opens your mod.
* **Published** *(Required)*: Whether the page is visible to readers. Unpublished pages are saved as drafts — visible only to you and your collaborators, regardless of the mod's visibility setting.
### Editor
Below the settings you'll find two panels:
* **Content Editor**: Where you write your page content in Markdown.
* **Live Preview**: A real-time preview of how your page will look when published.
### Saving your page
At the bottom of the page you have three options:
* **Cancel**: Discard and go back without creating anything.
* **Save as draft**: Save the page without publishing it.
* **Publish**: Save and publish the page immediately.
***
You're ready to create pages! To learn how to format content and make your pages look great,
see [Styling your pages](./styling/index.mdx).
--- 2 - Creating a Wiki for Your Mod
Source: https://hytalemodding.dev/en/docs/wiki/2-create-mod
## 1. Creating a mod
Start by going to the [dashboard](https://wiki.hytalemodding.dev/dashboard).
If you haven't created any mods yet, you'll see a **"No mods yet"** message with a create button below it.
If you already have mods, they'll be listed there — click **New mod** to add another.
On the creation page, you'll see the following fields:
* **Mod name** *(Required)*: The name of your mod as it will appear on pages.
* **Description** *(Optional)*: A short description of what your mod does.
* **Mod Icon** *(Optional)*: An icon to represent your mod.
* **Visibility** *(Required, default: "Private")*: Controls who can see your mod.
### About visibility
* **Private**: Only you and your collaborators can see this mod.
* **Unlisted**: Accessible to anyone with the link, but won't appear in public listings.
* **Public**: Your mod will be listed [here](https://wiki.hytalemodding.dev/mods).
Since you're just getting started, **Private** or **Unlisted** is recommended.
Congratulations — you've created your first mod wiki!
## 2. Collaborators
If you're working with a team and want to give members editing access, you can add them as collaborators.
On your mod's main page, look for the collaborators panel in the bottom-left — initially it will only show you.
Click **Manage** to open the collaborator settings.
There you'll see the owner, a list of collaborators, and a reference to the available permission levels:
* **Owner**: Full control over everything. This role cannot be changed.
* **Admin**: Can create, edit, and delete pages. Can also manage collaborators and invite new members.
* **Editor**: Can create, edit, and publish pages. Cannot manage collaborators or mod settings.
* **Viewer**: Can view private mod content, but cannot edit or create pages.
To add someone, click **Invite Collaborators**, enter their username, and select a role.
They'll receive an email invitation — once they accept, they'll appear in your collaborators list.
If you plan on creating pages on GitHub, check out the [guide there](./6-github).
--- Wiki
Source: https://hytalemodding.dev/en/docs/wiki
## What is Hytale Modding Wiki
Hytale Modding Wiki is a free platform where mod developers can create and maintain documentation for their mods.
It gives you a straightforward way to publish guides, explain features, and document how your mod works —
making it easier for both players and developers to understand and use your project.
In this guide, you will learn how to:
* create a wiki for your mod,
* edit and organize pages,
* structure your documentation effectively,
* keep it up to date over time.
## Why use this platform
You could host documentation on GitHub, your own website, or another platform —
but Hytale Modding Wiki is built specifically for mod documentation, so everything you need is already here.
--- 4 - Editing and Organizing Pages
Source: https://hytalemodding.dev/en/docs/wiki/4-editing-pages
## 1. Editing a page
To edit a page, click the pencil icon in your mod's page list, or click **Edit** while viewing a page.
Editing works the same as creating a page, with one difference — the buttons at the bottom:
* **Cancel**: Discard your changes and go back.
* **Delete Page**: Permanently delete this page.
* **Save as draft**: Save the page without publishing it.
* **Publish**: Save and publish the page.
## 2. Organizing your pages
### Nesting pages
You can nest pages under a parent to create a hierarchy — set the **Parent page** field when creating or editing a page.
### Reordering pages
To change the order pages appear in the sidebar, go to your mod's management page,
click **View all Pages**, then **Reorder Pages**.
Drag pages into the desired order and click **Exit** when done.
***
You now know how to edit pages and control how they're arranged.
--- Interactions
Source: https://hytalemodding.dev/en/docs/server/interaction-reference
### The Interaction Base Type
All interactions of every type below are inherited from Interaction and have these fields.
**Interaction Fields**
| Field Name | Type | Required? | Notes |
| ------------------------- | -------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ViewDistance | `Double` (Default: 96.0) | **No** | Distance in blocks at which other players can see the effects from this interaction. |
| Effects | `InteractionEffects` | **No** | Sound, animations, and particle effects that should trigger when this interaction begins executing. |
| HorizontalSpeedMultiplier | `Float` (Default: 1.0) | **No** | Multiplier applied to the User entity's movement speed while this interaction is executing |
| RunTime | `Float` | **No** | If provided, this interaction will continue executing for at least this long before the interaction chain moves onto the next interaction. |
| CancelOnItemChange | `Boolean` (Default: true) | **No** | If true, this interaction will be cancelled if the User entity's held item changes during execution. This can happen either due to the selected hotbar slot changing or the contents of the item's inventory slot changing. |
| Rules | `InteractionRules` | **Yes** | If provided, adds additional limitations and cancellation conditions to this interaction. |
| Settings | `Map` (Key Type: `GameMode`, Value Type: `InteractionSettings`) | **No** | If provided, adds additional per-GameMode settings to the interaction. |
| Camera | `InteractionCameraSettings` | **No** | Arrays of camera motion keyframes for cutscenes and reveals. |
**GameMode Values**
* `Creative`: The game is currently in Creative Mode
* `Adventure`: The game is currently in Adventure Mode
**InteractionEffects Fields**
| Field Name | Type | Required? | Notes |
| ------------------------ | ------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Particles | `Array` (Element Type: `ModelParticle`) | **No** | Particle systems to activate when this interaction begins if the player is in third-person mode. |
| FirstPersonParticles | `Array` (Element Type: `ModelParticle`) | **No** | Particle systems to activate when this interaction begins if the player is in first-person mode. |
| WorldSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | A sound to play in the world at the User entity's location when this interaction begins. **The SoundEvent must be single-channel (mono) sound.** |
| LocalSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | A 2D sound to play for the User entity when this interaction begins. Ignored if the User entity is not a player. |
| Trails | `Array` (Element Type: `ModelTrail`) | **No** | Trail effects to activate on the User entity when this interaction begins. |
| WaitForAnimationToFinish | `Boolean` (Default: false) | **No** | If true, this interaction will continue executing for at least the length of any animations in this InteractionEffects object. |
| ItemPlayerAnimationsId | `Asset` (Asset Type: `ItemPlayerAnimations`) | **No** | If provided, the User entity will use this ItemPlayerAnimations set while this interaction is executing. |
| ItemAnimationId | `String` | **No** | Id of an animation to trigger when this interaction begins executing. **This animation id is not validated on server start.** |
| ClearAnimationOnFinish | `Boolean` (Default: false) | **No** | If true, animations triggered by this interaction will be halted when the interaction completes. |
| ClearSoundEventOnFinish | `Boolean` (Default: false) | **No** | If true, sounds triggered by this interaction will be halted when the interaction completes. |
| CameraEffect | `Asset` (Asset Type: `CameraEffect` ) | **No** | If provided, the CameraEffect will be applied while this interaction is executing. |
| MovementEffects | `MovementEffects` | **No** | If provided, apply the movement effects to the User entity while this interaction is executing. |
| StartDelay | `Float` (Default: 0.0f) | **No** | The interaction will wait this many seconds before triggering the effects in this object. |
**InteractionRules Fields**
| Field Name | Type | Required? | Notes |
| ------------------- | --------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BlockedBy | `Array` (Element Type: `InteractionType`) (Default: Based on the chain's InteractionType) | **No** | If provided, this RootInteraction cannot be executed while interaction chains of the provided types are executing. **This field only functions for rules attached to a RootInteraction. It has no effect on an Interaction.** |
| Blocking | `Array` (Element Type: `InteractionType`) | **No** | If provided, interaction chains with the specified InteractionTypes cannot be executed while this interaction is executing. |
| InterruptedBy | `Array` (Element Type: `InteractionType`) | **No** | If provided, interaction chains with the specified InteractionTypes will cancel this interaction chain when they execute. **This field only functions for rules attached to a RootInteraction. It has no effect on an Interaction.** |
| Interrupting | `Array` (Element Type: `InteractionType`) | **No** | If provided, interaction chains with the specified InteractionTypes will be canceled when this interaction begins. |
| BlockedByBypass | `String` | **No** | If provided, this RootInteraction cannot be blocked by interaction chains whose RootInteraction has this value as an asset tag. |
| BlockingBypass | `String` | **No** | If provided, this interaction will not block RootInteractions that have the this value as an asset tag. |
| InterruptedByBypass | `String` | **No** | If provided, this RootInteraction cannot be canceled by RootInteractions that have this value as an asset tag. |
| InterruptingBypass | `String` | **No** | If provided, this interaction will not cancel interaction chains whose RootInteraction has this value as an asset tag. |
**InteractionType Values**
* `Primary`
* `Secondary`
* `Ability1`
* `Ability2`
* `Ability3`
* `Use`
* `Pick`
* `Pickup`
* `CollisionEnter`
* `CollisionLeave`
* `Collision`
* `EntityStatEffect`
* `SwapTo`
* `SwapFrom`
* `Death`
* `Wielding`
* `ProjectileSpawn`
* `ProjectileHit`
* `ProjectileMiss`
* `ProjectileBounce`
* `Held`
* `HeldOffhand`
* `Equipped`
* `Dodge`
* `GameModeSwap`
**InteractionSettings Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | -------------------------------------------------------------------------------------------- |
| AllowSkipOnClick | `Boolean` (Default: false) | **No** | If true, the user will be able to skip this interaction by clicking shortly after it starts. |
**InteractionCameraSettings Fields**
| Field Name | Type | Required? | Notes |
| ----------- | ------------------------------------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FirstPerson | `Array` (Element Type: `InteractionCamera`) | **No** | A list of camera keyframes to be played if the player is in first-person. **The time of each keyframe must be after the keyframe before it in the array.** |
| ThirdPerson | `Array` (Element Type: `InteractionCamera`) | **No** | A list of camera keyframes to be played if the player is in third-person. **The time of each keyframe must be after the keyframe before it in the array.** |
**InteractionCamera Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Time | `Float` (Default: 0.1f) | **No** | How long after the interaction begins executing that the camera should arrive at this keyframe. **Cannot be less than or equal to 0.** |
| Position | `Vector3` | **Yes** | Where the camera should be located when it arrives at this keyframe. |
| Rotation | `Direction` | **Yes** | Where the camera should be pointing when it arrives at this keyframe. |
### SimpleInteraction
Most interaction types below are inherited from SimpleInteraction and have these
fields. If a type does not have these fields, the documentation will mention it.
The purpose of SimpleInteraction is to facilitate Interaction chaining by taking different paths
based on whether the Interaction succeeded or failed. The circumstances under which
a given Interaction fails are generally type-specific (and described in the documentation
below), but all interactions will be marked failed if they are cancelled via one of the
many, many mechanisms for doing so.
It is valid to create an interaction asset of type "Simple", which can be valuable when you want to use the
interaction fields with no additional functionality.
**SimpleInteraction Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------------------- | --------- | ---------------------------------------------------------------------------------------------- |
| Next | `Asset` (Asset Type: `Interaction`) | **No** | After this Interaction has finished, "Next" will be executed if this Interaction did not fail. |
| Failed | `Asset` (Asset Type: `Interaction`) | **No** | After this Interaction has finished, "Failed" will be executed if this Interaction failed. |
## Flow Control
These are interaction types used to control the flow of an interaction chain and allow interactions
to be composed into more complex behaviors. These interaction types generally do not pertain to
the game and instead pertain to the interaction system itself.
### Condition
This interaction will fail if one of the fields is provided but does not match the current state.
**Condition Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ---------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| RequiredGameMode | `GameMode` | **No** | If provided, the interaction will fail if the current GameMode does not match the provided value. |
| Jumping | `Boolean` | **No** | If provided, the interaction will fail if the player's Jumping state does not match the provided value. (i.e. if True, the player must be jumping, if False, the player must not be) |
| Swimming | `Boolean` | **No** | If provided, the interaction will fail if the player's Swimming state does not match the provided value. (i.e. if True, the player must be swimming, if False, the player must not be) |
| Crouching | `Boolean` | **No** | If provided, the interaction will fail if the player's Crouching state does not match the provided value. (i.e. if True, the player must be crouching, if False, the player must not be) |
| Running | `Boolean` | **No** | If provided, the interaction will fail if the player's Running state does not match the provided value. (i.e. if True, the player must be running, if False, the player must not be) |
| Flying | `Boolean` | **No** | If provided, the interaction will fail if the player's Flying state does not match the provided value. (i.e. if True, the player must be flying, if False, the player must not be) |
**GameMode Values**
* `Creative`: The game is currently in Creative Mode
* `Adventure`: The game is currently in Adventure Mode
### FirstClick
This interaction does not inherit from SimpleInteraction and therefore has no Next or Failed fields.
This interaction has two fields that each contain a reference to an Interaction: Click and Hold. If the root interaction
of the chain was initiated by a client button/key press (or simulated NPC input) and the input was a tap, then
the Click interaction is run. if it was a longer press, then the Hold interaction is run.
As a minor implementation detail, this interaction is internally marked as failed when the Hold path is followed.
This generally shouldn't matter. If the interaction was not initiated by a key press at all, the Click path will be followed,
but it seems risky to rely on that behavior.
**FirstClick Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Click | `Asset` (Asset Type: `Interaction`) | **No** | If provided and the root interaction of the chain was initiated by button/key press, this interaction is executed next if the press was a quick tap. |
| Hold | `Asset` (Asset Type: `Interaction`) | **No** | If provided and the root interaction of the chain was initiated by a button/key press, this interaction is executed next if the press was a longer hold. |
### Interrupt
This interaction can be used to cancel one or more running interaction chains on an entity and **all forked interaction
chains those chains have spawned that are still currently running**. All interaction chains canceled in this manner will be marked as
Failed, which may have additional implications if the canceled chains are themselves forked from
other chains that are still running.
**Interrupt Fields**
| Field Name | Type | Required? | Notes |
| -------------- | ---------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Entity | `InteractionTarget` (Default: User) | **Yes** | The entity that is the owner of the target interaction chains. This field can indicate the Owner, User, or Target of this interaction chain. |
| InterruptTypes | `Array` (Element Type: `InteractionType`) | **No** | A set of InteractionTypes that are to be interrupted. Only interactions of these types are affected. If null, all types will be interrupted. **(Note: An empty set will interrupt NO TYPES.)** |
| RequiredTag | `String` | **No** | An asset tag that, if provided, the RootInteraction of the interaction chain must have in order to be canceled. |
| ExcludedTag | `String` | **No** | An asset tag that, if provided, the RootInteraction of the interaction chain must not have in order to be canceled. |
**InteractionTarget Values**
* `User` - The user of this interaction chain. The entity whose actions caused this interaction chain to be executed. This is usually the same as the Owner.
* `Owner` - The owner of this interaction chain. The entity upon whom this interaction chain is executing.
* `Target` - The entity target of this interaction chain, if any. This value is mutable (and can therefore come from many places) but most commonly, it is an entity the User was targeting when this interaction chain began executing.
**InteractionType Values**
* `Primary`
* `Secondary`
* `Ability1`
* `Ability2`
* `Ability3`
* `Use`
* `Pick`
* `Pickup`
* `CollisionEnter`
* `CollisionLeave`
* `Collision`
* `EntityStatEffect`
* `SwapTo`
* `SwapFrom`
* `Death`
* `Wielding`
* `ProjectileSpawn`
* `ProjectileHit`
* `ProjectileMiss`
* `ProjectileBounce`
* `Held`
* `HeldOffhand`
* `Equipped`
* `Dodge`
* `GameModeSwap`
### Parallel
This interaction does not inherit from SimpleInteraction and therefore has no Next or Failed fields.
This interaction has a field `Interactions` that is an array of RootInteraction assets. The list must contain at least
two RootInteractions. This interaction will fork all of its children except for the first into their
own interaction chains and immediately complete. After this interaction completes, this interaction chain will
execute the first RootInteraction in the `Interactions` list.
This interaction does not have any failure conditions and is indifferent to the fate of the forked interaction chains.
**Parallel Fields**
| Field Name | Type | Required? | Notes |
| ------------ | --------------------------------------------------- | --------- | ------------------------------------------------------------- |
| Interactions | `AssetArray` (Element Type: `RootInteraction`) | **Yes** | An array of RootInteractions that will be forked or executed. |
### Repeat
This interaction has a field `ForkInteractions` that is an asset of type RootInteraction. This interaction will fork that
RootInteraction into its own interaction chain and then wait for it to complete. If the RootInteraction fails, this
interaction will complete immediately and be marked failed. Otherwise, the RootInteraction will be forked and run again,
up to a specified total number of times.
**Repeat Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | -------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ForkInteractions | `Asset` (Asset Type: `RootInteraction`) | **Yes** | A RootInteraction that will be forked. |
| Repeat | `Integer` (Default: 1) **Must be -1 or a positive integer. 0 is not valid.** | **No** | The total number of times to fork ForkInteractions. If -1 is provided, ForkInteractions will continue being forked and executed indefinitely, until this interaction is cancelled or until ForkInteractions fails. |
### Replace
This interaction does not inherit from SimpleInteraction and therefore has no Next or Failed fields.
This interaction uses the InteractionVars system that is avaiable for items. InteractionVars are RootInteractions that
can be attached to an Item asset with a name.
This interaction specifies a var name, and if this interaction chain was executed through
an item and that item has an InteractionVar of the same name, then this interaction will immediately execute
the RootInteraction on the var. Otherwise, it will execute the default RootInteraction specified in this interaction.
If no default is specified, then this interaction will be marked as failed.
**Replace Fields**
| Field Name | Type | Required? | Notes |
| ------------ | -------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Var | `String` | **Yes** | The name of the InteractionVar to execute. |
| DefaultValue | `Asset` (Asset Type: `RootInteraction`) | **No** | The RootInteraction to execute if the owning item does not have an InteractionVar by the specified name. If not provided, the interaction will be marked as failed instead. |
| DefaultOk | `Boolean` (Default: false) | **No** | If false, then an angry message will be printed to the server logs if the owning item does not have an InteractionVar by the specified name. The functioning of this interaction will be otherwise unaffected. |
### RunRootInteraction
This interaction immediately executes the provided RootInteraction as part of this interaction chain. This interaction
does not have any failure conditions.
**RunRootInteraction Fields**
| Field Name | Type | Required? | Notes |
| --------------- | -------------------------------------------- | --------- | ------------------------------- |
| RootInteraction | `Asset` (Asset Type: `RootInteraction`) | **Yes** | The RootInteraction to execute. |
### Selector
This interaction will execute a Selector to find entities and/or blocks (depending on the Selector) and fork off an
interaction chain for each located entity or block. This interaction will not wait for those interaction chains
to complete and is indifferent to their fate. The forked interaction chains will use the located entity/block as
their target entity/target block. Each entity or block will only fork one interaction chain over the life of this
interaction. The Selector is guaranteed to run once for each execution of this interaction, but some Selectors
are intended to be run over a period of time and sweep across an area to locate targets. In order to use those Selectors
properly, you will need to extend the execution time of this interaction using `RunTime` or `WaitForAnimationToFinish`.
The Selector will be executed repeatedly over the lifetime of the interaction.
If the Selector is configured to locate entities, you can add additional sets of entity matching rules. If a located entity
matches one of the rules, the matching RootInteraction will be forked instead of the standard `HitEntity` RootInteraction.
Entities located by the Selector will be ignored if they do not have a NetworkId, if they are dead, or if they are invulnerable.
The only exception to this rule is that players in Creative Mode that are marked to receive hits will not be ignored.
This interaction's failure conditions are configurable via the `FailOn` field. You can configure this interaction to never
fail, to fail if the Selector does not locate any blocks before this interaction finishes executing, to fail if the
Selector does not locate any entities before this interaction finishes executing, or to fail in either case.
**Selector Fields**
| Field Name | Type | Required? | Notes |
| -------------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Selector | `Selector` | **Yes** | The Selector is executed at least once and may be executed repeatedly to locate blocks and/or entities in an area. |
| HitEntity | `Asset` (Asset Type: `RootInteraction`) | **No** | If included and the `Selector` locates an entity that does not match any `HitEntityRules`, this RootInteraction will be forked into a new interaction chain with the located entity as the target entity. |
| HitEntityRules | `Array` (Element Type: `HitEntity`) | **No** | If included, these rules will be matched against any entity located by the `Selector`. If an entity matches a set of rules, the RootInteraction associated with the rules will be forked instead of the standard `HitEntity` interaction. |
| HitBlock | `Asset` (Asset Type: `RootInteraction`) | **No** | If included and the `Selector` locates a block, this RootInteraction will be forked into a new interaction chain with the located block as the block target. |
| FailOn | `FailOnType` (Default: `Neither`) | **No** | Defines under what circumstances this interaction will fail. It can be marked to fail when it completes without locating any blocks, it can be marked to fail when it completes without locating any entities, both, or neither. |
| IgnoreOwner | `Boolean` (Default: true) | **No** | If true, the Selector will not locate the User entity of this interaction. |
**HitEntity Fields**
| Field Name | Type | Required? | Notes |
| ---------- | -------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------- |
| Matchers | `Array` (Element Type: `EntityMatcher`) | **Yes** | A set of rules that an entity must match. An entity must successful match all rules to be considered. |
| Next | `Asset` (Asset Type: `RootInteraction`) | **Yes** | The RootInteraction to fork if an entity matches all rules. |
**EntityMatcher Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------- |
| Type | `String` | **Yes** | If provided, specifies a type of EntityMatcher to use. Each matcher type may come with additional fields. |
| Invert | `Boolean` (Default: false) | **No** | If true, an entity must fail this rule for it to be marked as succeeded. |
**EntityMatcher Type: Player**
This EntityMatcher will succesfully match the entity only if they are a player.
**EntityMatcher Type: Vulnerable**
This Entitymatcher will successfully match the entity only if they are not marked as invulnerable.
**FailOnType Values**
* `Neither`
* `Entity`
* `Block`
* `Either`
### Serial
This interaction does not inherit from SimpleInteraction and therefore has no Next or Failed fields.
This interaction has a field `Interactions` which is a list of Interaction assets. This interaction immediately
completes and the interaction chain continues by executing the interactions in the list one at a time. This
interaction does not have any failure conditions.
**Serial Fields**
| Field Name | Type | Required? | Notes |
| ------------ | ----------------------------------------------- | --------- | ------------------------------------------ |
| Interactions | `AssetArray` (Element Type: `Interaction`) | **Yes** | An array of Interaction assets to execute. |
## Cooldowns
RootInteractions interface with a complex cooldown system that can be used for a variety of purposes. Aside from simply
adding an important aspect of resource management to your game design, cooldowns can be used to ensure that click-activated
interaction chains don't repeatedly spam if the player holds down the input.
By default, every RootInteraction has a cooldown timer that is keyed to the RootInteraction name, and by default, it
will launch a cooldown of 0.35 seconds when activated by client input. Otherwise, no cooldown will be activated. However,
by configuring your RootInteraction, you can change the key for the cooldown (so that multiple RootInteractions can share
a single cooldown), set the cooldown timer to whatever you like, and configure multiple charges that can build up over time
to allow the entity to activate the RootInteraction multiple times before being forced to pause.
One important concept to keep in mind is the difference between **cooldown** and **charge time**. The cooldown, if any,
is activated each time a RootInteraction triggers, even if the player has more available charges. If the RootInteraction
is configured to use charges, then they will gradually build up over time. In order for a RootInteraction to be activated,
the cooldown (if any) must be expired, and the RootInteraction must have at least one charge (if it has been
configured to use charges).
Bear in mind that the entire Cooldown configuration is shared between different RootInteractions with the same cooldown id.
If you activate an ability with 5 charges and a short cooldown and then another ability with a much longer cooldown and no
charges, the second ability will use the cooldown properties of the first ability until/unless the first ability finishes
charging and is cleared from the system. You should generally only have multiple RootInteractions share a cooldown id
if they have the same configuration.
The interactions below interact with the cooldown system to allow you to manipulate cooldowns via the interaction system. By
using these interactions, you can have several interaction chains with different cooldown configurations share cooldowns
that are checked and activated as part of the interaction chain itself, or have some components of an interaction change
use a second cooldown separate from the main one.
### CooldownCondition
**CooldownCondition Fields**
This interaction will fail if the specified cooldown id is on cooldown for the User entity. This interaction will only
function for players- the interaction will always succeed for NPCs. In order for this interaction to succeed, the
cooldown, if any, must have expired and at least one charge, if any, must have filled.
| Field Name | Type | Required? | Notes |
| ---------- | -------- | --------- | ------------------------- |
| Id | `String` | **Yes** | The cooldown id to check. |
### IncrementCooldown
**IncrementCooldown Fields**
This interaction will modify a cooldown that is currently active on the User entity, allowing charges and remaining
cooldown to be modified.
Despite its name, this interaction can be used to reduce a cooldown as well as increase it. If the cooldown is not
currently active, this interaction will have no effect. The interaction has no special failure conditions.
| Field Name | Type | Required? | Notes |
| ----------------- | --------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Id | `String` | **No** | If provided, the cooldown id of the cooldown to be manipulated. If not provided, the cooldown id of the current chain's RootInteraction will be used. |
| Time | `Float` (Default: 0.0) | **No** | The amount to increase the remaining cooldown by. Can be negative. The remaining cooldown time will not be reduced below zero or increased above the maximum cooldown time. |
| ChargeTime | `Float` (Default: 0.0) | **No** | The amount to increase the remaining time until the next charge is filled by. Can be negative. The remaining charge time cannot be reduced below zero or increased above the maximum cooldown time. **This will not work on abilities that have a maximum of 1 charge, for some reason.** |
| Charge | `Integer` (Default: 0) | **No** | The amount to increase the number of charges by. Can be negative. The number of charges cannot be reduced below 0 or increased above the maximum. |
| InterruptRecharge | `Boolean`(Default: false) | **No** | If true and `Charge` is not 0, will reset the remaining time to the next charge to the maximum value after changing the charge count. |
### ResetCooldown
This interaction will trigger a cooldown on the User entity with a specified configuration. The charges will be refilled
but the cooldown will be immediately triggered. The mechanisms for deciding on the configuration are a little bit complicated.
You may specify a cooldown configuration via the `Cooldown` field. If you do not include an `Id` field, then the
interaction chain's RootInteraction will be used for any field not specified in the configuration. If there is an active
cooldown under the specified `Id` (or under the RootInteraction's cooldown id if none is specified) then the fields
from that active cooldown will overwrite data in the RootInteraction's cooldown, but not data in your `Cooldown` field.
**ResetCooldown Fields**
| Field Name | Type | Required? | Notes |
| ---------- | --------------------- | --------- | ------------------------------------------------------------ |
| Cooldown | `InteractionCooldown` | **No** | A cooldown configuration to use for the new, reset cooldown. |
**InteractionCooldown Fields**
| Field Name | Type | Required? | Notes |
| ----------------- | ------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Id | `String` | **No** | The cooldown id to use for this cooldown. If not provided, the RootInteraction's cooldown id will be used, if any. |
| Cooldown | `Float` (Default: 0.0) | **No** | The duration of the cooldown, in seconds. |
| Charges | `Array` (Element Type: `Float`) | **No** | If provided, a list of how many charges this cooldown can build, and how long each charge takes to build, in seconds. |
| InterruptRecharge | `Boolean` (Default: false) | **No** | If true, the charge time will be reset when the number of charges changes. |
| ClickBypass | `Boolean` (Default: false) | **No** | If true, RootInteractions keyed to this cooldown can still be activated when it is on cooldown provided that the RootInteraction was triggered by a distinct key or mouse button press on the client. |
### TriggerCooldown
This interaction is similar to ResetCooldown, except instead of refilling the cooldown's charges, one charge will be deducted
as normal.
**TriggerCooldown Fields**
| Field Name | Type | Required? | Notes |
| ---------- | --------------------- | --------- | ------------------------------------------------------------ |
| Cooldown | `InteractionCooldown` | **No** | A cooldown configuration to use for the new, reset cooldown. |
**InteractionCooldown Fields**
| Field Name | Type | Required? | Notes |
| ----------------- | ------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Id | `String` | **No** | The cooldown id to use for this cooldown. If not provided, the RootInteraction's cooldown id will be used, if any. |
| Cooldown | `Float` (Default: 0.0) | **No** | The duration of the cooldown, in seconds. |
| Charges | `Array` (Element Type: `Float`) | **No** | If provided, a list of how many charges this cooldown can build, and how long each charge takes to build, in seconds. |
| InterruptRecharge | `Boolean` (Default: false) | **No** | If true, the charge time will be reset when the number of charges changes. |
| ClickBypass | `Boolean` (Default: false) | **No** | If true, RootInteractions keyed to this cooldown can still be activated when it is on cooldown provided that the RootInteraction was triggered by a distinct key or mouse button press on the client. |
## Combo Chains
Hytale additionally comes with a rich "combo chaining" system, which allows several inputs in a row to result in different
attacks and special moves. Rather than being attached to the RootInteraction like the cooldown system, these are
primarily driven by the `Chaining` interaction.
### Chaining
This interaction does not inherit from SimpleInteraction and therefore has no Failed field. Its Next field works differently
from most interaction types.
Chaining is a type of flow control interaction- each time a Chaining interaction with the same `ChainId` is called, a single
interaction in the `Next` list is chosen and the others are ignored. The first time the `ChainId` is used, the first
interaction in the list is chosen, the next time the second, etc. Once the end of the list is reached, the first
interaction will be chosen again.
Bear in mind: the progress through the Next list is based on the **ChainId**, not the interaction! That means that two
different Chaining interactions can share a single combo chain. You could have a pair of daggers that attacks with
the left when the left mouse button is clicked and with the right when the right mouse button is clicked. You could give
both interaction chains a Chaining interaction with the same `ChainId` and three `Next` entries that do a light-light-heavy
combo. Each specific hit would occur with either the left or the right knife based on which button you clicked, but
the combo would be otherwise identical regardless of which you chose.
The Chaining interaction also has a concept called "flags". It is possible to attach named flags to a `ChainId` by using
the ChainFlag interaction. Then, you specify the flag name in Chaining's `Flags` field. If a Flag is active when the
Chaining interaction is run, the Flag's interaction will be run instead of the appropriate interaction in `Next`.
Bear in mind: the entity's progress through the `Next` list still advances, the resulting `Next` interaction is just not
run. Only one flag can be active at a time.
Lastly, it is possible to cancel the entity's progress through the combo chain and reset them to the first interaction in
the list. This can be done with the CancelChain interaction, but it will also happen automatically if the player does
not trigger an interaction with the ChainId for the specified ChainingAllowance. When either of these happen, all flags will be
canceled as well.
All of these effects are triggered on the User entity only. Non-players can use the chaining system, but Flags do not currently
work for non-players. This interaction has no special failure conditions.
**Chaining Fields**
| Field Name | Type | Required? | Notes |
| ----------------- | ---------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ChainId | `String` | **No** | If provided, acts as a key that multiple Chaining interactions can use to share combo chain progress and flags. If not provided, a unique ChainId will be generated for this interaction. |
| ChainingAllowance | `Double` (Default: 0.0) | **No** | If, at the time this interaction is triggered, it has been this long in seconds since the last time a Chaining interaction with the same ChainId was executed, all state for this ChainId will be cleared before execution. |
| Next | `AssetArray` (Element Type: `Interaction`) | **Yes** | A list of interactions to cycle between each time this interaction is run. |
| Flags | `Map` (Key Type: `String`, Value Type: `Interaction`) | **No** | A map of flag names to interactions to run if the flag is active when this interaction executes. |
### CancelChain
This interaction resets all progress and flag data on the User entity for the provided `ChainId`. The next time a Chaining
interaction is called for the provided `ChainId`, it will begin at the first interaction in the `Next` list. This
interaction has no special failure conditions.
**CancelChain Fields**
| Field Name | Type | Required? | Notes |
| ---------- | -------- | --------- | ------------------------------------- |
| ChainId | `String` | **Yes** | A ChainId whose data should be reset. |
### ChainFlag
This interaction activates a flag on the User entity for the provided `ChainId`, which can be used to change behavior the
next time a Chaining interaction with this `ChainId` is run. This will overwrite any flag that has
already been raised. This interaction will have no effect for non-players. This interaction has no special failure
conditions.
| Field Name | Type | Required? | Notes |
| ---------- | -------- | --------- | -------------------------------------------- |
| ChainId | `String` | **Yes** | A ChainId on whom the flag should be raised. |
| Flag | `String` | **Yes** | The name of the flag to raise. |
## Charging
A unique flow-control interaction that is frequently used is the Charging interaction. It is a flow-control interaction
that delays while the input button used to trigger the interaction chain is held. When it is released (or the interaction
is forced to end), the interaction chain can continue with a different interaction based on how long the input was held
for.
These interactions are all related by either being the Charging interaction, or other interaction types that inherit the
Charging interaction's behavior.
### Charging
This interaction does not inherit from SimpleInteraction and therefore its Next field works different from most interactions.
This interaction adds its own Failed field which works the same way that SimpleInteraction's does.
This interaction only works properly when its interaction chain was triggered by a client key or button press (or
NPC-simulated equivalent). This interaction will continue executing for as long as the key or button press is held (with
some exceptions, covered below) by the User entity. Like other interactions, it has a `Next` field indicating which interaction to execute
after this one, but the field is a map from `Float` values to `Interaction` assets. When this interaction ends, either
due to the input being released or the charge duration completing, this interaction will determine the largest `Float` key that is less
than the amount of time in seconds that the interaction was executing for. The Interaction mapped to that key will be
executed next.
The interaction includes some cancellation conditions that are specific to this interaction. For instance, the interaction
has a `CancelOnOtherClick` field that matches the field on RootInteraction. If it is active on this interaction but not
the RootInteraction, then clicking while charging will cancel this interaction (and execute the `Failed` interaction rather
than any of the `Next` interactions) but otherwise continue the interaction chain.
Another valuable feature Charging has is the `Forks` field. For input-triggered interactions that are triggered while the
Charging interaction is executing, you can add an entry for the relevant InteractionType to this field and instead of
triggering the usual interaction chain based on held item, targeted block, etc. the RootInteraction specified in the
`Forks` field will be triggered. This can unlock rich attack modes and behaviors. For instance, having a shield bash attack
triggered off left click while blocking, or a special attack that triggers when both mouse buttons are pressed at the
same time.
There are two modes of operation for the Charging interaction. One is to have the ability charge for as long
as the input is held and only end execution when the entity releases the input. Bows in the main Hytale game work
that way. That is accomplished by setting `AllowIndefiniteHold` to true. Another mode of operation is to have the
interaction complete execution when the execution time has surpassed the largest key value in the `Next` field and
immediately begin executing the Interaction mapped to that key. Food in the main Hytale game work like this. It
is accomplished by setting `AllowIndefiniteHold` to false.
One pitfall to be careful of when setting `AllowIndefiniteHold` to false is the way it interacts with cooldowns.\
Normally, input-initiated interaction chains start a 0.35s cooldown on the triggered RootInteraction that prevents
holding the mouse button from immediately spamming a new interaction when the first one completes. However, if the
duration of a Charging interaction is longer than 0.35s, then the cooldown will have already completed when the
interaction chain finishes, which will often result in a new execution beginning immediately before the player can
stop pressing the mouse button. One solution for this problem is to add a `Simple` interaction to the end of your
interaction chain with a `RunTime` of 0.35s to simulate the cooldown. Alternatively, you can add a longer cooldown
(the maximum duration of your Charging interaction plus 0.35s) to the RootInteraction. However, the longer cooldown is
not always desirable, as the interaction may have ended prematurely, resulting in an unusually long lockout between
executions.
**Charging Fields**
| Field Name | Type | Required? | Notes |
| ---------------------------------- | ----------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FailOnDamage | `Boolean` (Default: false) | **No** | If true, this interaction will fail and end immediately if the User entity takes any damage while it is executing. |
| CancelOnOtherClick | `Boolean` (Default: true) | **No** | If true, this interaction will fail and end immediately if the User entity sends another button or key press while it is executing. |
| Forks | `Map` (Key Type: `InteractionType`, Value Type: `RootInteraction`) | **No** | A map of InteractionType to RootInteraction. If entity input triggers the InteractionType while this interaction is executing, the mapped RootInteraction will be forked into its own interaction chain. This will happen instead of the usual handling for that InteractionType. This fork will occur even if `CancelOnOtherClick` is set to true. |
| Failed | `Asset` (Asset Type: `Interaction`) | **No** | After this Interaction has finished, "Failed" will be executed if this Interaction failed. |
| AllowIndefiniteHold | `Boolean` (Default: false) | **No** | If true, this interaction will continue executing for as long as the User entity continues to hold the input. If false, execution will end as soon as the execution time in seconds reaches the largest key value in the `Next` map. |
| DisplayProgress | `Boolean` (Default: true) | **No** | If true, players executing this interaction will see a progress bar will be displayed below their cursor indicating how close to reaching the highest key value in `Next` they are. |
| Next | `Map` (Key Type: `Float`, Value Type: `Interaction`) | **No** | A map from `Float` to `Interaction`. When this interaction successfully finishes executing (either because the input was released or because the maximum duration was reached), this interaction will find the `Float` value with the largest duration in seconds that is less than the amount of time this interaction executed for. That key's mapped Interaction will then be executed. |
| MouseSensitivityAdjustmentTarget | `Float` (Default: 1.0) | **No** | While charging, this mouse sensitivity multiplier will gradually be applied. |
| MouseSensitivityAdjustmentDuration | `Float` (Default: 1.0) | **No** | Duration in seconds. After this many seconds, `MouseSensitivityAdjustmentTarget` will be fully applied to the mouse sensitivity. |
| Delay | `ChargingDelay` | **No** | Settings that allow charging progress to be pushed back when the User entity takes damage. |
**ChargingDelay Fields**
| Field Name | Type | Required? | Notes |
| ------------- | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MinHealth | `Float` (Default: 0.0) | **No** | Damage taken as a percentage of entity health (0.0-1.0). If a hit deals less than this much damage, no pushback will be applied. If a hit deals exactly this much damage, `MinDelay` seconds worth of pushback will be applied. |
| MaxHealth | `Float` (Default: 0.0) | **No** | Damage taken as a percentage of entity health (0.0-1.0). If a hit deals at least this much damage, `MaxDelay` seconds worth of pushback will be applied. |
| MinDelay | `Float` (Default: 0) | **No** | The amount of pushback, in seconds, to apply if the entity takes a hit that deals `MinHealth` damage. The amount of pushback applied by a hit is a gradiant between `MinDelay` and `MaxDelay` based on the amount of damage between `MinHealth` and `MaxHealth`. |
| MaxDelay | `Float` (Default: 0) | **No** | The amount of pushback, in seconds, to apply if the entity takes a hit that deals at least `MaxHealth` damage. The amount of pushback applied by a hit is a gradiant between `MinDelay` and `MaxDelay` based on the amount of damage between `MinHealth` and `MaxHealth`. |
| MaxTotalDelay | `Float` (Deault: 0) | **No** | The maximum amount of pushback the entity can receive over the course of a single charge, regardless of how many times they are hit. |
**InteractionType Values**
* `Primary`
* `Secondary`
* `Ability1`
* `Ability2`
* `Ability3`
* `Use`
* `Pick`
* `Pickup`
* `CollisionEnter`
* `CollisionLeave`
* `Collision`
* `EntityStatEffect`
* `SwapTo`
* `SwapFrom`
* `Death`
* `Wielding`
* `ProjectileSpawn`
* `ProjectileHit`
* `ProjectileMiss`
* `ProjectileBounce`
* `Held`
* `HeldOffhand`
* `Equipped`
* `Dodge`
* `GameModeSwap`
### Wielding
This interaction is inherited from Charging rather than SimpleInteraction. However, its `Failed` and `Next` fields function
identically to SimpleInteraction. It does not inherit all fields from Charging, so be sure to pay close attention to
the field list below.
This interaction drives most blocking behavior in the game. While this interaction is executing, attacks made against
the Owner entity will (conditionally) trigger effects defined in this interaction. Unlike the Charging interaction,
this interaction can always execute indefinitely and will only end when it fails or when the input is released.
One option available in Wielding is `AngledWielding`, which allows damage and knockback modifiers to only be applied
depending on the direction the attack against the Owner entity is coming from. The AngledWielding type has a
field `Angle` which determines which direction the protection points relative to the player (0 is the player's
front), and `AngleDistance` which determines how wide the cone of protection extends around the player. Block effects
from this interaction (such as `BlockedEffects`, `BlockedInteractions`, and `StaminaCost`) will only apply if
the DamageCause matches `DamageModifiers` OR if the DamageCause matches `AngledWielding.DamageModifiers` and
the source of the damage matches `Angle` and `AngleDistance`. It is possible for damage and knockback multipliers
specified on both this interaction's modifier fields and `AngledWielding`'s modifier fields to stack, if both
have entries for the same DamageCause.
This interaction will only have an effect on damage taken by one of the DamageCause entries in `DamageModifiers` or
`AngledWielding.DamageModifiers`. If a DamageCause is in `DamageModifiers`, then the block effect will be triggered
and the damage modifier applied regardless of what direction the Owner entity is attacked from. If a DamageCause is in
`AngledWielding.DamageModifiers`, then the damage modifier is only applied if the
\*\* Wielding Fields\*\*
| Field Name | Type | Required? | Notes |
| ------------------- | ----------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FailOnDamage | `Boolean` (Default: false) | **No** | If true, this interaction will fail and end immediately if the User entity takes any damage while it is executing. |
| CancelOnOtherClick | `Boolean` (Default: true) | **No** | If true, this interaction will fail and end immediately if the User entity sends another button or key press while it is executing. |
| Forks | `Map` (Key Type: `InteractionType`, Value Type: `RootInteraction`) | **No** | A map of InteractionType to RootInteraction. If entity input triggers the InteractionType while this interaction is executing, the mapped RootInteraction will be forked into its own interaction chain. This will happen instead of the usual handling for that InteractionType. This fork will occur even if `CancelOnOtherClick` is set to true (after which this interaction chain will be canceled). |
| Next | `Asset` (Asset Type: `Interaction`) | **No** | After this interaction has finished, "Next" will be executed if this interaction did not fail. |
| Failed | `Asset` (Asset Type: `Interaction`) | **No** | After this interaction has finished, "Failed" will be executed if this interaction failed. |
| DamageModifiers | `Map` (Key Type: `DamageCause`, Value Type: `Float`) | **No** | A map from DamageCause to Float, where the Float is multiplied against any damage received from the mapped DamageCause while this interaction is executing. |
| KnockbackModifiers | `Map` (Key Type: `DamageCause`, Value Type: `Float`) | **No** | A map from DamageCause to Float, where the Float is multiplied against any knockback force received from the mapped DamageCause while this interaction is executing. |
| AngledWielding | `AngledWielding` | **No** | Configuration to only trigger block effects from certain angles. |
| StaminaCost | `StaminaCost` | **No** | If the Owner entity receives damage that matches `DamageModifiers` or `AngledWielding` while this interaction is executing, then the entity's stamina will be deducted according to this configuration. |
| BlockedEffects | `DamageEffects` | **No** | If the Owner entity receives damage that matches `DamageModifiers` or `AngledWielding` while this interaction is executing, then these effects will be applied to the instance of damage. |
| BlockedInteractions | `Asset` (Asset Type: `RootInteraction`) | **No** | If the Owner entity receives damage that matches `DamageModifiers` or `AngledWielding` while this interaction is executing, then this RootInteraction will be launched as a brand new interaction chain with the Owner entity as both Owner and User, and the attacker as the Target entity. This new interaction chain is, notably, brand new, **not** forked from the current one. |
**AngledWielding Fields**
| Field Name | Type | Required? | Notes |
| ------------------ | --------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Angle | `Float` (Default: 0.0) | **No** | Angle in degrees relative to the Owner entity where the angled protection is centered. 0.0 means pointed towards the front of the Owner entity. |
| AngleDistance | `Float` (Default: 0.0) | **No** | Angle in degrees indicating how wide the cone of protection is. |
| DamageModifiers | `Map` (Key Type: `DamageCause`, Value Type: `Float`) | **No** | A map from DamageCause to Float, where the Float is multiplied against any damage received from the mapped DamageCause received from within the cone of protection while this interaction is executing. |
| KnockbackModifiers | `Map` (Key Type: `DamageCause`, Value Type: `Float`) | **No** | A map from DamageCause to Float, where the Float is multiplied against any knockback force received from the mapped DamageCause received from within the cone of protection while this interaction is executing. |
**StaminaCost Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CostType | `CostType` (Default: `MaxHealthPercentage`) | **No** | How to calculate stamina consumed by blocking from the raw pre-block damage. If `Damage`, then the stamina consumed will be `rawDamage / Value`. If `MaxHealthPercentage`, then the stamina consumed will be `rawDamage / (EntityMaxHealth * Value)` |
| Value | `Float` (Default: 0.04) | **No** | The value to use in the `CostType` formula. |
**DamageEffects Fields**
| Field Name | Type | Required? | Notes |
| ---------------------- | -------------------------------------------- | --------- | ------------------------------------------------------------------------------- |
| ModelParticles | `Array` (Element Type: `ModelParticle`) | **No** | Particle systems to trigger in the first-person view. |
| WorldParticles | `Array` (Element Type: `WorldParticle`) | **No** | Particle systems to trigger in the third-person view. |
| LocalSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | 2D sound to play locally to the attacking entity if they are a player. |
| WorldSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | Sound to play in the world at the Owner entity's location. |
| PlayerSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | 2D sound to play locally to the Owner entity if they are a player. |
| ViewDistance | `Double` (Default: 75.0) | **No** | Distance at which these effects should play for other players. |
| Knockback | `Knockback` | **No** | The properties of the knockback that will be applied. |
| CameraEffect | `Asset` (Asset Type: `CameraEffect`) | **No** | CameraEffect to briefly apply locally to the Owner entity if they are a player. |
| StaminaDrainMultiplier | `Float` (Default: 1.0) | **No** | This multiplier is applied to the `StaminaCost`, if any. |
**Knockback Fields**
| Field Name | Type | Required? | Notes |
| -------------- | ------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| Type | `String` | **No** | If provided, specifies a type of knockback to use. Each knockback type may come with additional fields. |
| Force | `Double` (Default: 0.0) | **No** | The amount of force to apply to the Owner entity. |
| Duration | `Double` (Default: 0.0) | **No** | If 0.0, apply the force as a single impulse. If more, continuously apply the force over a period of time. |
| VelocityType | `ChangeVelocityType` (Default: `Add`) | **No** | How to apply the force, either adding it to the entity's current velocity, or setting the velocity to the knockback force. |
| VelocityConfig | `VelocityConfig` | **No** | Characteristics of friction as the Owner entity is thrown by this knockback. |
**Knockback Type: Force**
Apply the calculated knockback force along the provided direction.
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------- | --------- | ------------------------------------------------------------------------------------ |
| Direction | `Vector3` (Default: Up) | **No** | Which direction the player should be flung, relative to the direction of the attack. |
**Knockback Type: Point**
Apply the calculated knockback force laterally along the XZ plane, plus provided Y velocity.
| Field Name | Type | Required? | Notes |
| ---------- | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| OffsetX | `Integer` (Default: 0) | **No** | Offset the source position of the knockback left or right perpendicular to the direction of the attack. |
| OffsetZ | `Integer` (Default: 0) | **No** | Offset the source position of the knockback, forward or backward along the directon of the attack. |
| RotateY | `Integer` (Default: 0) | **No** | Rotate the knockback direction around the Y axis, where 0 is the default attack direction. |
| VelocityY | `Float` (Default: 0.0) | **No** | Add vertical velocity to the knockback force after the direction and force has been calculated. |
**Knockback Type: Directional**
Apply the calculated knockback force along the attack direction, removing any vertical component and replacing it with a provided Y velocity.
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| RelativeX | `Float` (Default: 0) | **No** | Apply additional force left or right perpendicular to the direction of the attack. The additional force is multiplied by the knockback strength. |
| RelativeZ | `Float` (Default: 0) | **No** | Apply additional force forward or backward along the attack direction. The additional force is multiplied by the knockback strength. |
| VelocityY | `Float` (Default: 0) | **No** | Replace the vertical velocity of the knockback with this value. This value is not multiplied by knockback strength. |
**VelocityConfig Fields**
| Field Name | Type | Required? | Notes |
| ------------------- | ------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GroundResistance | `Float` (Default: 0.82) | **No** | The minimum amount of friction applied when the entity is in contact with the ground. 1.0 = no friction, 0.0 = total friction |
| GroundResistanceMax | `Float` (Default: 0.0) | **No** | The maximum amount of friction applied when the entity is in contact with the ground. 1.0 = no friction, 0.0 = total friction |
| AirResistance | `Float` (Default: 0.96) | **No** | The minimum amount of friction applied when the entity is in the air. 1.0 = no friction, 0.0 = total friction |
| AirResistanceMax | `Float` (Default: 0.0) | **No** | The maximum amount of friction applied when the entity is in the air. 1.0 = no friction, 0.0 = total friction |
| Threshold | `Float` (Default: 1.0) | **No** | The speed at which the maximum amount of friction will be applied to the entity. The amount of friction applied will increase from minimum to maximum as the entity moves from 0 to this speed. |
| Style | `VelocityThresholdStyle` (Default: `Linear`) | **No** | The curve that friction follows as the entity's speed increases toward the `Threshold`. |
**CostType Values**
* `MaxHealthPercentage`
* `Damage`
**ChangeVelocityType Values**
* `Add`
* `Set`
**VelocityThresholdStyle Values**
* `Linear`
* `Exp`
## Blocks
These are interaction types meant to reason about and modify blocks in the world. The most important concept unique
to these interactions is the Block Target. Like the Entity Target, it is mutable and can come from many places.
However, for interaction chains initiated by the client, the block target will initially be the block that the player's cursor was
over, if any.
Additionally, for interaction chains initiated by the client, the client will send along which face of the block is targeted,
which is used by a few block-related interactions.
It is left up to each interaction type that cares about block faces as to how to handle this behavior for simulated input
(i.e. NPCs initiating chains that are normally initated by the client). At the time of this writing, all block-related
interactions choose the top face where relevant in simulated input.
### BlockCondition
This interaction has a field `Matchers` which specifies certain conditions, such as block type, state, etc. This
interaction fails if the current target block does not match all of them.
**BlockCondition Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| Matchers | `Array` (Element Type: `BlockMatcher`) | **Yes** | A list of conditions that the target block must match or this condition will fail. **Note: This field is technically not required but there is a bug that makes it dangerous to not provide at the time of this writing.** **Note: If no conditions are added to this array, the interaction will fail, counterintuitively.** |
**BlockMatcher Fields**
| Field Name | Type | Required? | Notes |
| ---------- | -------------------------- | --------- | ------------------------------------------------------------------------------ |
| Block | `BlockIdMatcher` | **No** | If provided, requires the block itself match some set of conditions. |
| Face | `BlockFace` | **No** | If provided and not 'None', requires a specific block face be targeted. |
| StaticFace | `Boolean` (Default: false) | **No** | If false, the face condition will be adjusted by the block's current rotation. |
**BlockFace Values**
* `Up`
* `Down`
* `North`
* `South`
* `East`
* `West`
* `None`
**BlockIdMatcher Fields**
| Field Name | Type | Required? | Notes |
| ---------- | -------------------------------------- | --------- | -------------------------------------------------------------------------- |
| Id | `Asset` (Asset Type: `BlockType`) | **No** | If provided, the block must match the specified `BlockType`. |
| State | `String` | **No** | If provided, the block's current state must match the provided state name. |
| Tag | `String` | **No** | If provided, the block type's asset tags must contain the provided tag. |
### DestroyCondition
This interaction fails if the current User entity cannot destroy the current target blcok.
**DestroyCondition Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
### PlacementCountCondition
This interaction accepts the name of a BlockType that has the TrackedPlacement BlockEntity. The interaction will fail if the number of
blocks of the provided BlockType in the world do not match the provided condition.
**PlacementCountCondition Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Block | `String` | **Yes** | The BlockType to examine. The number of instances of the BlockType in the world will be compared to Value. **Note: The specified BlockType must have the TrackedPlacement BlockEntity. Otherwise, the number of blocks in the world will always be zero.** |
| Value | `Integer` (Default: 0) | **No** | The value to compare the number of blocks in the world with. |
| LessThan | \`Boolean (Default: true) | **No** | If true, the interaction will fail if the number of blocks in the world is greater than or equal to Value. If false, the interaction will fail if the number of blocks in the world is less than or equal to Value. |
### BreakBlock
This interaction attempts to break the target block while acting as the User entity. The User entity must be a player. It
is not guaranteed to work, depending on world settings and the User entity's current state, it may not work. If it
fails, it will fail the interaction under some circumstances, but not others.
**Note: In particular, if `Harvest`=false and the player is in Survival mode, the player will simply execute a damage operation
against the block. Even when the Tool field is specified, the player's currently-held item can impact the results in unintuitive
ways.**
This interaction will fail if there is no block target, if `Harvest`=true and the
block is not harvestable, or if the world settings prevent the Harvest/Break operation.
It will generally not fail otherwise, even if the block was not broken or damaged.
The block will generally break every time if `Harvest`=true or the player is in
Creative mode.
**BreakBlock Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| Harvest | `Boolean` (Default: false) | **No** | If true, harvest the block and deposit the drops in the player's inventory. This operation will generally always succeed. If false, this interaction will effectively perform whatever action would have occurred if the player had left-clicked the block. |
| Tool | `String` | **No** | If `Harvest`=true, the player is in Survival mode, and this field is provided, then the break operation will be performed as though with this tool type. **This field mostly doesn't work. Your held item will be used for most block damage calculations regardless of this field's contents.** |
| MatchTool | `Boolean` (Default: false) | **No** | If true, `Harvest`=true, the player is in Survival mode, and `Tool` is provided, block breaking will be prevented unless `Tool` is a valid gathering tool for the target block. The interaction will not be failed, even if block breaking is prevented in this manner. |
### ChangeBlock
This interaction requires a mapping from one or more current BlockTypes to one or more target BlockTypes. If the
target block is one of the key BlockTypes, then it will be changed to the mapped target BlockType. If the current
BlockType of the target block does not appear as a key in the Changes field, then this interaction fails.
**ChangeBlock Fields**
| Field Name | Type | Required? | Notes |
| ----------------- | ----------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| Changes | `Map` (Key Type: `BlockType`, Value Type: `BlockType`) | **No** | A list of potential block type changes, with each entry having a current and new BlockType id. |
| WorldSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | If provided, this SoundEvent is played in the world at the position of the block if the block successfully changes. |
| RequireNotBroken | `Boolean` (Default: false) | **No** | If true, this interaction will fail if the player's held item is broken (durability 0), and the block change will not take place. |
### ChangeState
This interaction requires a mapping from one or more current block state names to one or more state names. If the
target block is in one of the key block states, then it will be changed to the mapped target blocks tate. If the
current block state of the target block does nto appear as a key in the Changes field, then this interaction fails.
All blocks start out with a null state by default. This null state can be referenced by the key "default" and the block
can be returned to that state with the value "default".
**ChangeState Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ----------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| Changes | `Map` (Key Type: `String`, Value Type: `String`) | **No** | A list of potential block state changes, with each entry having a current and new state name. |
| UpdateBlockState | `Boolean` (Default: false) | **No** | If true, performs a full refresh of the block's BlockEntity component and notifies nearby players as though a full block type change occurred. |
### CycleBlockGroup
This interaction will attempt to change the target block to the next block type in one of the BlockGroups it belongs to.
If the block type belongs to multiple BlockGroups, which one is used is effectively arbitrary. The User entity must be
a player. If the block cannot be changed for any reason (entity not a player, no block target, block does not belong
to a BlockGroup, etc.). If the block is the only entry in the BlockGroup, this interaction will succeed and the block
will be considered changed despite nothing happening. Unlike most other block-changing interactions, this one will
fail if the world settings prevent players from breaking blocks.
**This interaction always reduces the player's held item's durability by a single 'hit' worth of damage if the block
changes. There is no way to prevent it. The block type will change and the interaction will succeed even if the player's
held item is broken, however.**
**CycleBlockGroup Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
### DestroyBlock
This interaction destroys the target block without dropping anything. It will successfully destroy the block if the
target block exists and this interaction has no failure conditions.
**This interaction has no additional fields.**
### PickBlock
This interaction is effectively equivalent to using the middle mouse button in creative mode. In survival mode, the
block's item will be moved to the user's current active hotbar slot if it is in the player's inventory. This
interaction is performed entirely on the client and cannot be performed by non-players. It is unclear if this
interaction has failure conditions.
**PickBlock Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
### PlaceBlock
This interaction is effectively identical to a right clicking while holding a block in their hand, with some
additional options. For one, it can be executed with any LivingEntity as the User entity. For another, it has some
useful fields. For the most part, though, it follows the same behaviors and rules as any ordinary block placement
by a player.
It's not clear under what circumstances this interaction can fail. The server will not fail the interaction under any
special circumstances (including when there is no target block, or when `BlockTypeToPlace` is not provided and the
LivingEntity is not holding a block). However, the client is very involved with the function of this interaction and
its behavior is unknown.
**PlaceBlock Fields**
| Field Name | Type | Required? | Notes |
| ------------------ | -------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BlockTypeToPlace | `Asset` (Asset Type: `BlockType`) | **No** | If provided, the block placed by this interaction will be the specified BlockType. If not provided, the User entity's held item will be used, instead, if it is a block. |
| RemoveItemInHand | `Boolean` (Default: True) | **No** | If true and the User entity is not a player in Creative mode, the item in the player's hand must match the block being placed and one quantity will be removed for each block placed. If the item does not match, the block cannot be placed. If false, the player's hand item is ignored entirely.\*\* |
| AllowDragPlacement | `Boolean` (Default: True) | **No** | If true and the interaction was triggered on the client by a player, then the player can hold the input that was used to activate this interaction chain and move the mouse around in order to execute this interaction repeatedly and place several blocks. The interaction chain will not move onto the next interaction until the player releases the input. |
### RunOnBlockTypes
This interaction retrieves all blocks within a provided radius of the User entity. For each block that matches any of
the provided BlockSets, a new interaction chain will be forked that uses the matching block as the target block.
This interaction will continue executing until all forked interaction chains have completed. The interaction will fail if no
blocks within the radius match the provided BlockSets or if all of the forked interaction chains fail.
**RunOnBlockTypes Fields**
| Field Name | Type | Required? | Notes |
| ------------ | -------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Range | `Integer` | **Yes** | The spherical radius around the User entity to search for blocks. Must be at least 1. |
| BlockSets | `AssetArray` (Element Type: `BlockSet`) | **No** | A list of BlockSets to match nearby blocks again. Only blocks that match any of the BlockSets will have interaction chains forked for them. |
| MaxCount | `Integer` | **Yes** | The maximum number of blocks that will be located with the search. Further blocks will be ignored. If there are more matching blocks within the radius than this number, the specific set of blocks that will be found is completely arbitrary and may change from execution to execution. |
| Interactions | `Asset` (Asset Type: `RootInteraction`) | **No, but yes.** | The RootInteraction that will be forked for each matching block. This interaction will not fail to validate if it is missing, but every execution of this interaction will fail. |
### UseBlock
If the target block has an interaction for this interaction chain's interaction type, execute it immediately as part of
this interaction chain. The execution will be wrapped in UseBlockEvent.Pre and UseBlockEvent.Post events, and cancellations
of the UserBlockEvent.Pre event are respected. The User entity is considered the triggering entity for both events.
If the target block does not have an interaction for this interaction chain's interaction type, this interaction fails.
**This interaction has no additional fields.**
## Items
These are interactions intended to interface with an entity's inventory. There is no concept of an "item target",
unfortunately, so these generally either interact with the entity's held item or the entity's inventory at a high level.
### AddItem
Adds a specified quantity of a specified item to the User entity's inventory, preferring to add it to the
entity's hotbar where possible. This interaction will have no effect on User entities that are not LivingEntities.
This interaction has no failure states.
**Note: This interaction has a bug in its field validators that will cause interactions that reference items declared
in the same mod to fail validation on startup. Additionally, this interaction has a bug that will cause it to
fail and have no effect if the User entity has no block target. Use ModifyInventory instead.**
**AddItem Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | --------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| ItemId | `Asset` (Asset Type: `Item`) | **Yes** | The item id of the item to add to the player's inventory. |
| Quantity | `Integer` (Default: 0) | **No** | The quantity of the item to add to the player's inventory. |
### CheckUniqueItemUsage
This interaction will check whether it has run once for the User entity with their currently-held item before. If it hasn't,
it will succeed. If it has, it will fail. It will also fail if the User entity is not a player. The interaction will record
the item id of the User entity's currently-held item on success, ensuring it cannot be successfully run with that player
and item id combination again.
**This interaction has no additional fields.**
### ChangeActiveSlot
This interaction does not inherit from SimpleInteraction and therefore has no Next or Failed fields. The `CancelOnItemChange`
field is ignored for this interaction and is always treated as false. This interaction will set the User entity's currently-
active hotbar slot to the specified slot index. If none is specified, the interaction chain's target slot will be used,
if any. Otherwise, slot 0 will be used. If a slot is specified and it is already active, then this interaction will
have no effect, but if no slot is specified and the interaction chain's target slot is already active, the logic in
the next paragraph will still be executed. This interaction will also have no effect if the User entity is not a
LivingEntity.
Once the slot has changed, a new SwapTo interaction chain will be forked off for the selected slot.
If a target slot was specified, then the interaction chain's current target slot will be set to the specified slot, as will
the forked SwapTo interaction chain. If this interaction chain was initiated by a player swapping an item into or out of
an occupied active hotbar slot, then the swap will be executed after the slot is changed and before the SwapTo
interaction is forked. Not running this interaction in the resulting SwapFrom interaction chain is a way for the server
to cancel players performing these sorts of swap interactions. The default behavior for these swap interactions is that
the SwapFrom interaction chain will be initiated with the currently-active-slot as the target slot. As a result, the
target slot will usually not change when this interaction runs, it merely exists as a signal for the server to continue the
item swap.
**ChangeActiveSlot Fields**
| Field Name | Type | Required? | Notes |
| ---------- | --------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TargetSlot | `Integer` | **No** | If not provided, indicates the slot the player's active hotbar slot should be changed to. Otherwise, the interaction chain's target slot will be used (which is usually the already-active hotbar slot). |
### EquipItem
This interaction will equip the User entity's currently-held item into the appropriate armor slot, if it is armor. The
interaction will fail if the item is armor but it cannot be equipped for some reason. The interaction will succeed but
take no action if the User entity cannot equip armor or if the item is not armor.
**This interaction has no additional fields.**
### IncreaseBackpackCapacity
This interaction will increase the User entity's backpack size by the provided amount. **It will additionally remove one
quantity from the player's currently-held item. There is no way to prevent this.** This interaction will have no effect
if the User entity is not a player. This interaction has no special failure conditions, even if the User entity
is not a player, or the player is not currently holding an item.
| Field Name | Type | Required? | Notes |
| ---------- | --------------------------- | --------- | --------------------------------------------------------------------------------------- |
| Capacity | `Integer` (Default: 1) | **No** | The additional backpack capacity to add to the player. **Must be between 1 and 32767.** |
### ModifyInventory
This interaction attempts to perform one or more inventory-modifying actions against the User entity. The User entity must
be a player. If they are not, no actions will be performed, but the interaction will not fail. Any item removals performed
by this interaction, either as a result of `ItemToRemove` or `AdjustHeldItemQuantity`, are both atomic (meaning the entire
quantity must be removed, or none) and required (meaning that the interaction will fail and no further actions taken if
the removal fails for any reason). Item additions performed by this interaction, either as a result of `ItemToAdd` or
`AdjustHeldItemQuantity` will drop excess items on the ground near the player if necesary and continue on.
This interaction first executes the item removal in `ItemToRemove`, if any, then adjusts the held item as specified by
`AdjustHeldItemQuantity`, if provided, then executes the item addition in `ItemToAdd`, if any, and then finally executes
the damage/healing of the held item in `AdjustHeldItemDurability`, if provided.
**ModifyDurability Fields**
| Field Name | Type | Required? | Notes |
| ------------------------ | ------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| RequiredGameMode | `GameMode` | **No** | If provided, only execute the inventory-modifying actions if the player is in the provided GameMode. The interaction will not fail if the GameMode doesn't match. |
| ItemToRemove | `ItemStack` | **No** | If provided, a matching item will be removed in the provided quantity. If there is no matching item in the player inventory in the required quantity, then the interaction will fail and further actions will not be executed. |
| AdjustHeldItemQuantity | `Integer` (Default: 0) | **No** | The player's held item, if any, will have its quantity will be modified by the provided amount- subtracted from if this number is negative or added to if this item is positive. If the player has no held item, then this field will have no effect regardless of its value and the interaction will NOT fail. If the player's inventory does not have sufficient quantity of items to support the removal, this interaction will fail and further actions will not be executed. Excess items will be dropped on the ground near the player. |
| ItemToAdd | `ItemStack` | **No** | If provided, the ItemStack will be added to the player's inventory. Excess items will be dropped on the ground near the player. |
| AdjustHeldItemDurability | `Double` (Default: 0.0) | **No** | The player's held item, if any, will have its durability adjusted by the specified amount. The durability cannot go below 0 or raise above the item's maximum durability. Invalid durability values in this field will not cause the interaction to fail. Neither will it fail if the player has no held item. |
| BrokenItem | `String` | **No** | This field should contain an ItemId, but it is not validated by the server on startup, so be careful. This field can have the value of "Empty" to specify no item, or an empty hand. If specified and the `AdjustHeldItemDurability` operation reduces the player's held item to 0 durability, the player's held item is changed to this ItemId. If the inventory replacement fails, this interaction will fail. |
| NotifyOnBreak | `Boolean` (Default: false) | **No** | If true and the `AdjustHeldItemDurability` operation reduces the player's held item to 0 durability, this interaction will perform the usual audiovisual feedback of a broken tool. It will play the usual tool breaking sound and send the player a text message in chat that their item has broken. |
| NotifyOnBreakMessage | `String` | **No** | If provided, and `NotifyOnBreak` is true, when the player is notified of a broken tool by this interaction, the message sent to their chat will instead be the translation key specified by this field. The translation key supports the templating parameter `{itemName}`. See the translation key `server.general.repair.itemBroken_Hoe` for an example. |
**GameMode Values**
* `Creative`: The game is currently in Creative Mode
* `Adventure`: The game is currently in Adventure Mode
**ItemStack Fields**
| Field Name | Type | Required? | Notes |
| ---------------------------- | --------------------------------- | --------- | ------------------------------------------------------------------------------------ |
| Id | `Asset` (Asset Type: `Item`) | **Yes** | The id of the Item asset this stack contains. |
| Quantity | `Integer` (Default: 1) | **No** | The number of items in this stack. **Must be greater than 0.** |
| Durability | `Double` (Default: 0.0) | **No** | The durability of the item stack. **Cannot be negative.** |
| MaxDurability | `Double` (Default: 0.0) | **No** | The maximum durability of the item this stack contains. **Cannot be negative.** |
| Metadata | `Bson Document` | **No** | If provided, applies the bson to the item stack metadata. |
| OverrideDroppedItemAnimation | `Boolean`\*(Default: false) | **No** | ??? |
### PickupItem
This interaction will attempt to pick up the User entity's current target entity and add it to their inventory. It will
fail if the User entity is not a player, if they do not have a target entity, or if the target entity is not an item
located in the world. It will not fail if the player simply cannot pick up the item.
**This interaction has no additional fields.**
### RefillContainer
This interaction will fail if the User entity is not a player. The interaction will draw a ray from the player's head
down their look direction until the edge of the player's held item's interaction range or until the first solid block.
The nearest fluid block of a type listed in this interaction's `States` map will result in the one of the player's
currently-held items being converted to the fluid's related item state. If the `States` map indicates a durability
for the new item state, then the converted item will use that durability. If the item is already in the fluid's related
item state, then this interaction will fail if the player's held item is already at maximum durability (unless the
`States` map indicates a durability value greater than the item's maximum, in which case the interaction will succeed
but the item will not change). Otherwise, the durability listed for the fluid in the `States` map will be added to the
item's current durability.
If the interaction has not failed by this point, the fluid block in the world may be transformed to a different type if one is listed
in the `States` map.
If any part of this process does not proceed for any reason (for instance, if the player is not holding an item),
then this interaction will fail.
**RefillContainer Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| States | `Map` (Key Type: `String`, Value Type: `RefillState`) | **Yes** | A map from item state names to `RefillState` objects, which contain information such as acceptable fluid block types, durability increases, and what fluid type to convert the targeted fluid block to after refill, if any. If the player targets a fluid block that matches one of the fluid types in the `RefillState`, the item will be converted to the keyed item state and the `RefillState` will be used to decide what happens next. |
**RefillState Fields**
| Field Name | Type | Required? | Notes |
| -------------- | ------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AllowedFluids | `Array` (Element Type: `String`) | **Yes** | If any of these fluids are targeted by the player, the player's held item will be converted to the keyed item state and `Durability` and `TransformFluid` will be used. |
| Durability | `Double` (Default: -1.0) | **No** | If the player's held item changes item states, this value is the new item's durability, unless it is negative, in which case the item will begin at full durability. If the player's held item is already in the item state, and the item is not already at full durability, this value will be added to the item's durability. |
| TransformFluid | `String` | **No** | If provided, this field is a fluid id that the targeted fluid block should be transformed to if the player's held item is successfully updated. |
## Entities
### DamageEntity
This interaction does not inherit from SimpleInteraction, but adds its own Next and Failed fields that
work similarly.
This interaction will cause the User entity to damage the Target entity. It has the capability to apply different
damage based on the direction of the attack or what body part was targeted by the User entity, with each possibility
providing different damage calculations, effects, and Next interaction. If multiple entires match the attack, `TargetedDamage`
(body part damage) takes highest priority, then the first matching `AngledDamage` in the list, then the default
damage in the base interaction fields. It is possible for elements of these different damage priorities to be taken peacemeal if higher-priority
damage fields do not overwrite lower-priority damage fields. For instance,
damage calculation could be taken from `TargetedDamage`, the damage effects could be taken from the first matching
`AngledDamage`, and the Next interaction coming from the default damage field.
If all damage dealt by this interaction is cancelled, then this interaction will fail, and the `Failed` interaction
will be executed next in the chain. If any damage dealt by this interaction is blocked, then this interaction will
succeed, but the `Blocked` interaction wil lbe executed next in the chain. Otherwise, the highest-precedent matching
`Next` interaction that you provide will be executed. For instance, if you provide `Next` interactions for all
`TargetedDamage` and `AngledDamage` entries, and one in the interaction's `Next` field, then a headshot could result
in a unique `Next` interaction being executed, even if there is a matching `AngledDamage` entry. However, if you leave
the `Next` interaction out of your `TargetedDamage` entries, a special `Next` interaction for backstabs could be
executed. Otherwise, the `Next` field in the root of the interaction will be executed instead.
DamageEntity has an unusual relationship with `RunTime`. This interaction always ends immediately after its first tick,
regardless of the status of `RunTime` or `WaitForAnimationToFinish`. However, if damage dealt by this interaction is
calculated as `DamageCalculatorType` `Dps`, then the `RunTime` will be used to determine the precise amount of damage to
deal. If `RunTime` is 0, then no damage will be dealt.
**DamageEntity Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DamageCalculator | `DamageCalculator` | **No** | If provided, describes the damage amounts and types to apply to the Target entity. |
| DamageEffects | `DamageEffects` | **No** | If provided, describes particles, sounds, knockback, etc. to apply to the target entity on damage. |
| Next | `Asset` (Asset Type: `Interaction`) | **No** After this interaction has finished, "Next" will be executed if damage was dealt to the target and none was blocked. | |
| AngledDamage | `Array` (Element Type: `AngledDamage`) | **No** | If provided, allows alternative `DamageCalculator`, `DamageEffects`, and `Next` entries for attacks that approach the target from certain angles. |
| TargetedDamage | `Map` (Key Type: `String`, Value Type: `TargetedDamage`) | **Yes** | This field must be provided, but is not required to have any entries. This is a map from body part names to `TargetedDamage` values. Hypixel only uses one body part name: `Head`, and it is not clear whether there are others. If provided, allows alternative `DamageCalculator`, `DamageEffects`, and `Next` entries for attacks that damage specific enemy body parts. |
| EntityStatsOnHit | `Array` (Element Type: `EntityStatOnHit`) | **No** | If provided, allows this attack to reduce enemy stats other than health. Each individual damage application dealt by the same ability will apply stat reductions, at diminishing returns. All damage dealt in interaction chains forked by a Selector interaction will share the number of hits across all targets. |
| Failed | `Asset` (Asset Type: `Interaction`) | **No** | After this interaction has finished, "Failed" will be executed if no damage hits were applied to the player, or if all damage hits were cancelled. |
| Blocked | `Asset` (Asset Type: `Interaction`) | **No** | After this interaction has finished, "Blocked" will be executed if any damage hits were blocked by the target. |
**DamageCalculator Fields**
| Field Name | Type | Required? | Notes |
| ------------------------- | --------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Type | `DamageCalculatorType` (Default: Absolute) | **No** | Whether the damage being applied is `Absolute` or \`Dps'. |
| Class | `DamageClass` (Default: Unknown) | **No** | The type of attack producing the damage. Some weapon systems apply equipment modifiers based on this value. |
| BaseDamage | `Map` (Key Type: `DamageCause`, Value Type: `Float`) | **No** | A set of DamageCauses and how much base damage is dealt for each. |
| SequentialModifierStep | `Float` (Default: 0.0) | **No** | Value between 0.0-1.0. Each additional hit from a single ability will have its damage reduced by this much as a percentage. All damage dealt in interaction chains forked by a Selector interaction will share the number of hits across all targets. |
| SequentialModifierMinimum | `Float` (Default: 0.0) | **No** | Value between 0.0-1.0. `SequentialModifierStep` cannot reduce damage below this percentage regardless of the number of hits. |
| RandomPercentageModifier | `Float` (Default: 0.0) | **No** | A random value between +/- this amount multiplied by the base damage will be added to the base damage. |
**DamageEffects Fields**
| Field Name | Type | Required? | Notes |
| ---------------------- | -------------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
| ModelParticles | `Array` (Element Type: `ModelParticle`) | **No** | Particle systems to trigger in the first-person view. |
| WorldParticles | `Array` (Element Type: `WorldParticle`) | **No** | Particle systems to trigger in the third-person view. |
| LocalSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | 2D sound to play locally to the attacking entity if they are a player. |
| WorldSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | Sound to play in the world at the Target entity's location. |
| PlayerSoundEventId | `Asset` (Asset Type: `SoundEvent`) | **No** | 2D sound to play locally to the Target entity if they are a player. |
| ViewDistance | `Double` (Default: 75.0) | **No** | Distance at which these effects should play for other players. |
| Knockback | `Knockback` | **No** | The properties of the knockback that will be applied. |
| CameraEffect | `Asset` (Asset Type: `CameraEffect`) | **No** | CameraEffect to briefly apply locally to the Target entity if they are a player. |
| StaminaDrainMultiplier | `Float` (Default: 1.0) | **No** | This multiplier is applied to the stamina cost incurred by the target blocking, if any. |
**AngledDamage Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ---------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AngleDistance | `Float` (Default: 0) | **No** | The width in degrees of the matching arc around the Target entity. If the attack against the Target originates from within the arc, then the properties below will be applied to the attack, if provided. |
| Angle | `Float` (Default: 0.0) | **No** | The angle in degrees around the Target entity at which the matching arc is centered. |
| DamageCalculator | `DamageCalculator` | **No** | If provided and the attack against the Target entity originates from within the provided arc, this DamageCalculator will override the interaction's `DamageCalculator` field. |
| DamageEffects | `DamageEffects` | **No** | If provided and the attack against the Target entity originates from within the provided arc, this DamageEffects will override the interaction's `DamageEffects` field. |
| Next | `Asset` (Asset Type: `Interaction`) | **No** | If provided and the attack against the Target entity originates from within the provided arc, this Interaction will override the interaction's `Next` field. |
**TargetedDamage Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ---------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| DamageCalculator | `DamageCalculator` | **No** | If provided and this TargetedDamage was matched, this DamageCalculator will override the interaction's `DamageCalculator` field and the `DamageCalculator` field of any matching `AngledDamage`. |
| DamageEffects | `DamageEffects` | **No** | If provided and this TargetedDamage was matched, this DamageEffects will override the interaction's `DamageEffects` field and the `DamageEffects` field of any matching `AngledDamage`. |
| Next | `Asset` (Asset Type: `Interaction`) | **No** | If provided and this TargetedDamage was matched, this Interaction will override the interaction's `Next` field and the `Next` field of any matching `AngledDamage`. |
**EntityStatOnHit Fields**
| Field Name | Type | Required? | Notes |
| --------------------------- | -------------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EntityStatId | `Asset` (AssetType: `EntityStatType`) | **Yes** | Which entity stat to affect. |
| Amount | `Float` (Default: 0.0) | **No** | How much of the stat to apply for each damage application. Use negative numbers to reduce stats. |
| MultipliersPerEntitiesHit | `Array` (Element Type: `Float`, Default: `[1.0, 0.6, 0.4, 0.2, 0.1]`) | **No** | The diminishing returns curve to be applied for multiple damage applications by the same ability. Each hit's stat impact will be multiplied by the next value in the list. If there are more hits than array elements, subsequent hits will be multiplied by `MultiplierPerExtraEntityHit`. |
| MultiplierPerExtraEntityHit | `Float` (Default: 0.05) | **No** | If an ability carries out more damage applications than there are elements in the `MultipliersPerEntitiesHit` array, then subsequent stat values will be multiplied by this number. |
**Knockback Fields**
| Field Name | Type | Required? | Notes |
| -------------- | ------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| Type | `String` | **No** | If provided, specifies a type of knockback to use. Each Target type may come with additional fields. |
| Force | `Double` (Default: 0.0) | **No** | The amount of force to apply to the Owner entity. |
| Duration | `Double` (Default: 0.0) | **No** | If 0.0, apply the force as a single impulse. If more, continuously apply the force over a period of time. |
| VelocityType | `ChangeVelocityType` (Default: `Add`) | **No** | How to apply the force, either adding it to the entity's current velocity, or setting the velocity to the knockback force. |
| VelocityConfig | `VelocityConfig` | **No** | Characteristics of friction as the Owner entity is thrown by this knockback. |
**Knockback Type: Force**
Apply the calculated knockback force along the provided direction.
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------- | --------- | ------------------------------------------------------------------------------------ |
| Direction | `Vector3` (Default: Up) | **No** | Which direction the entity should be flung, relative to the direction of the attack. |
**Knockback Type: Point**
Apply the calculated knockback force laterally along the XZ plane, plus provided Y velocity.
| Field Name | Type | Required? | Notes |
| ---------- | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| OffsetX | `Integer` (Default: 0) | **No** | Offset the source position of the knockback left or right perpendicular to the direction of the attack. |
| OffsetZ | `Integer` (Default: 0) | **No** | Offset the source position of the knockback, forward or backward along the directon of the attack. |
| RotateY | `Integer` (Default: 0) | **No** | Rotate the knockback direction around the Y axis, where 0 is the default attack direction. |
| VelocityY | `Float` (Default: 0.0) | **No** | Add vertical velocity to the knockback force after the direction and force has been calculated. |
**Knockback Type: Directional**
Apply the calculated knockback force along the attack direction, removing any vertical component and replacing it with a provided Y velocity.
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| RelativeX | `Float` (Default: 0) | **No** | Apply additional force left or right perpendicular to the direction of the attack. The additional force is multiplied by the knockback strength. |
| RelativeZ | `Float` (Default: 0) | **No** | Apply additional force forward or backward along the attack direction. The additional force is multiplied by the knockback strength. |
| VelocityY | `Float` (Default: 0) | **No** | Replace the vertical velocity of the knockback with this value. This value is not multiplied by knockback strength. |
**VelocityConfig Fields**
| Field Name | Type | Required? | Notes |
| ------------------- | ------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GroundResistance | `Float` (Default: 0.82) | **No** | The minimum amount of friction applied when the entity is in contact with the ground. 1.0 = no friction, 0.0 = total friction |
| GroundResistanceMax | `Float` (Default: 0.0) | **No** | The maximum amount of friction applied when the entity is in contact with the ground. 1.0 = no friction, 0.0 = total friction |
| AirResistance | `Float` (Default: 0.96) | **No** | The minimum amount of friction applied when the entity is in the air. 1.0 = no friction, 0.0 = total friction |
| AirResistanceMax | `Float` (Default: 0.0) | **No** | The maximum amount of friction applied when the entity is in the air. 1.0 = no friction, 0.0 = total friction |
| Threshold | `Float` (Default: 1.0) | **No** | The speed at which the maximum amount of friction will be applied to the entity. The amount of friction applied will increase from minimum to maximum as the entity moves from 0 to this speed. |
| Style | `VelocityThresholdStyle` (Default: `Linear`) | **No** | The curve that friction follows as the entity's speed increases toward the `Threshold`. |
**DamageCalculatorType Values**
* `Absolute`
* `Dps`
**DamageClass Values**
* `Unknown`
* `Light`
* `Charged`
* `Signature`
**ChangeVelocityType Values**
* `Add`
* `Set`
**VelocityThresholdStyle Values**
* `Linear`
* `Exp`
### LaunchProjectile
This is just a worse version of Projectile and is slated to be removed. Use Projectile.
### Projectile
This interaction will spawn a new projectile at the User entity's eye position that will travel in its look direction.
| Field Name | Type | Required? | Notes |
| ---------- | --------------------------------------------- | --------- | ------------------------------------------------------ |
| Config | `Asset` (Asset Type: `ProjectileConfig`) | **No** | Configuration describing the projectile to be spawned. |
### RemoveEntity
Despawns the specified entity from the world. This interaction will have no effect if the specified entity is a player. This
interaction has no special failure conditions.
| Field Name | Type | Required? | Notes |
| ---------- | ---------------------------------------- | --------- | ---------------------- |
| Entity | `InteractionTarget` (Default: User) | **No** | The entity to despawn. |
**InteractionTarget Values**
* `User` - The user of this interaction chain. The entity whose actions caused this interaction chain to be executed. This is usually the same as the Owner.
* `Owner` - The owner of this interaction chain. The entity upon whom this interaction chain is executing.
* `Target` - The entity target of this interaction chain, if any. This value is mutable (and can therefore come from many places) but most commonly, it is an entity the User was targeting when this interaction chain began executing.
### SendMessage
This interaction sends a message to the Owner entity if that entity can receive messages. Otherwise, the message
is written to the server logs. If the entity is a player, the message will appear in the player's chat window.
\*\*SendMessage
| Field Name | Type | Required? | Notes |
| ---------- | -------- | --------- | --------------------------------------------------------------------------------------------------------------------- |
| Message | `String` | **No** | A text message to be sent to the Owner entity. If this field is not provided, `Key` is used instead. |
| Key | `String` | **No** | A translation key for a message to be sent to the Owner entity. If `Message` is provided, then this field is ignored. |
### UseEntity
This interaction is not in use by Hypixel and has some unusual qualities that mean it probably won't do what you want it
to. Do not use it.
This interaction will retrieve the entity currently targeted by the client (NOT the interaction chain's current Target
entity) and execute that entity's RootInteraction for this interaction chain's current interaction type. The
RootInteraction will be executed as part of this current interaction chain. None of the properties of this interaction
chain will be modified.
This interaction will fail if the client is not currently targeting an entity, or if no interaction for the current
interaction type exists on the entity.
## Stats
Hytale's resource system has been written about fairly extensively. You can use these interactions to modify stats in
response to actions or use stats as a cost for abilities.
### ChangeStat
This interaction applies a set of raw stat changes to the User entity. It has no special failure conditions.
| Field Name | Type | Required? | Notes |
| ------------- | ------------------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| StatModifiers | `Map` (Key Type: `EntityStatType`, Value Type: `Float`) | **Yes** | A set of stats to modify and what value to apply to them. |
| ValueType | `ValueType` (Default: Absolute) | **No** | Whether the values in the `StatModifiers` map represent flat values to apply to the stat total, or percentages (0.0-100.0). Percentage values are percentage of the difference between the maximum and minimum stat value. |
| Behaviour | `ChangeStatBehavior` (Default: Add) | **No** | Whether the values in the `StatModifiers` map represent deltas to add into the User entity's current stat values, or fixed values to set the entity's stats to. |
**ValueType Values**
* `Absolute`
* `Percent`
**ChangeStatBehaviour Values**
* `Add`
* `Set`
### ChangeStatWithModifier
This interaction is like ChangeStat, except an armor stat modifier can be chosen which will apply bonuses and penalties
to a stat from the User entity's equipped armor before it is applied.
| Field Name | Type | Required? | Notes |
| --------------------- | ------------------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| StatModifiers | `Map` (Key Type: `EntityStatType`, Value Type: `Float`) | **Yes** | A set of stats to modify and what value to apply to them. |
| ValueType | `ValueType` (Default: Absolute) | **No** | Whether the values in the `StatModifiers` map represent flat values to apply to the stat total, or percentages (0.0-100.0). Percentage values are percentage of the difference between the maximum and minimum stat value. |
| Behaviour | `ChangeStatBehavior` (Default: Add) | **No** | Whether the values in the `StatModifiers` map represent deltas to add into the User entity's current stat values, or fixed values to set the entity's stats to. |
| InteractionModifierId | `InteractionModifierId` | **Yes** | The armor interaction modifier to apply bonuses and penalties for. |
**ValueType Values**
* `Absolute`
* `Percent`
**ChangeStatBehaviour Values**
* `Add`
* `Set`
**InteractionModifierId Values**
* `Dodge`
### StatsCondition
This interaction will fail if the User entity cannot afford the specified stats costs, or if it does not have the
requested stats.
| Field Name | Type | Required? | Notes |
| ---------- | ------------------------------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Costs | `Map` (Key Type:` EntityStatType`, Value Type: `Float`) | **Yes** | A set of costs that the User entity needs to be able to afford for this interaction to succeed. |
| ValueType | `ValueType` (Default: Absolute) | **No** | Whether the values in the `Costs` map represetn flat values or percentages (0.0-100.0). Percentage values are percentage of the difference between the maximum and minimum value. |
| LessThan | `Boolean` (Default: false) | **No** | If true, this interaction will fail unless the User entity's current stat values are at or **below** the specified levels. If false, they must be at or **above** the specified levels. |
| Lenient | `Boolean` (Default: false) | **No** | If true and `LessThan` is true, and a player's minimum stat value is below zero, then the player can afford a stat cost if the current value of a stat if above 0. |
**ValueType Values**
* `Absolute`
* `Percent`
### StatsConditionWithModifier
This interaction is like StatsCondition, except an armor stat modifier can be chosen which will apply bonuses and penalties
to a stat's cost from the User entity's equipped armor before it is compared.
| Field Name | Type | Required? | Notes |
| --------------------- | ------------------------------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Costs | `Map` (Key Type:` EntityStatType`, Value Type: `Float`) | **Yes** | A set of costs that the User entity needs to be able to afford for this interaction to succeed. |
| ValueType | `ValueType` (Default: Absolute) | **No** | Whether the values in the `Costs` map represetn flat values or percentages (0.0-100.0). Percentage values are percentage of the difference between the maximum and minimum value. |
| LessThan | `Boolean` (Default: false) | **No** | If true, this interaction will fail unless the User entity's current stat values are at or **below** the specified levels. If false, they must be at or **above** the specified levels. |
| Lenient | `Boolean` (Default: false) | **No** | If true and `LessThan` is true, and a player's minimum stat value is below zero, then the player can afford a stat cost if the current value of a stat if above 0. |
| InteractionModifierId | `InteractionModifierId` | **Yes** | The armor interaction modifier to apply bonuses and penalties for. |
**ValueType Values**
* `Absolute`
* `Percent`
**InteractionModifierId Values**
* `Dodge`
## EntityEffects
EntityEffects are buff or debuff effects that can be applied to a LivingEntity. These can have a variety of effects from
movement speed changes to periodic damage.
### EffectCondition
This interaction will fail if the specified entity's EntityEffects do not match the provided conditions.
**EffectCondition Fields**
| Field Name | Type | Required? | Notes |
| --------------- | ------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Entity | `InteractionTarget` (Default: User) | **Yes** | The entity to examine for EntityEffects. This field can indicate the Owner, User, or Target of this interaction chain. |
| Match | `Match` (Default: All) | **No** | If `All`, then this interaction will fail if any EntityEffect in the `EntityEffectIds` is not on the specified entity. If `None`, then this interaction will fail if any EntityEffect IS on the specified entity. |
| EntityEffectIds | `AssetArray` (Element Type: `EntityEffect`) | **Yes** | The set of EntityEffects to check against the specified entity and `Match`. |
**InteractionTarget Values**
* `User` - The user of this interaction chain. The entity whose actions caused this interaction chain to be executed. This is usually the same as the Owner.
* `Owner` - The owner of this interaction chain. The entity upon whom this interaction chain is executing.
* `Target` - The entity target of this interaction chain, if any. This value is mutable (and can therefore come from many places) but most commonly, it is an entity the User was targeting when this interaction chain began executing.
**Match Values**
* `All` - All EntityEffects must be on the specified entity for this interaction to succeed.
* `None` - None of the EntityEffects must be on the specified entity for this interaction to succeed.
### ApplyEffect
This interaction will apply the specified EntityEffect to the specified Entity. It has no special failure conditions.
**ApplyEffect Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ----------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| Entity | `InteractionTarget` (Default: User) | **Yes** | The entity to apply the EntityEffect to. This field can indicate the Owner, User, or Target of this interaction chain. |
| EffectId | `Asset` (Asset Type: `EntityEffect`) | **Yes** | The effect to apply. |
**InteractionTarget Values**
* `User` - The user of this interaction chain. The entity whose actions caused this interaction chain to be executed. This is usually the same as the Owner.
* `Owner` - The owner of this interaction chain. The entity upon whom this interaction chain is executing.
* `Target` - The entity target of this interaction chain, if any. This value is mutable (and can therefore come from many places) but most commonly, it is an entity the User was targeting when this interaction chain began executing.
### ClearEntityEffect
This interaction will remove the specified EntityEffect from the specified Entity. It has no special failure conditions.
It will not fail if the Entity does not have the EntityEffect.
**ClearEntityEffect Fields**
| Field Name | Type | Required? | Notes |
| ---------- | ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------- |
| Entity | `InteractionTarget` (Default: User) | **Yes** | The entity to remove the EntityEffect from. This field can indicate the Owner, User, or Target of this interaction chain. |
| EffectId | `Asset` (Asset Type: `EntityEffect`) | **Yes** | The effect to remove. |
**InteractionTarget Values**
* `User` - The user of this interaction chain. The entity whose actions caused this interaction chain to be executed. This is usually the same as the Owner.
* `Owner` - The owner of this interaction chain. The entity upon whom this interaction chain is executing.
* `Target` - The entity target of this interaction chain, if any. This value is mutable (and can therefore come from many places) but most commonly, it is an entity the User was targeting when this interaction chain began executing.
## Farming
These are interaction types built to support the in-game farming system in Hytale. They are mostly block-related interactions
with a few exceptions.
### ChangeFarmingStage
If the target block is a farming block, this interaction will attempt to modify the current farming stage in one of
three ways. The interaction can optionally specify which StageSet to use for the new stage. If it is not specified,
the current StageSet will be used. If any part of the the process of changing the growth stage fails, this interaction
will fail, with the one exception that attempting to set the stage to the crop's existing stage will have no effect but
still succeed.
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| StageSet | `String` | **No** | If provided, set the block's current stage to one in the provided StageSet. Otherwise, use the crop's current StageSet. |
| Increase | `Integer` | **No** | If provided, increase the current stage by the specified amount. If the specified stage does not exist, the block will be set to the closest extant stage. |
| Decrease | `Integer` | **No** | If provided and `Increase` is not provided, decrease the current stage by the specified amount. If the specified stage does not exist, the block will be set to the closest extant stage. |
| Stage | `Integer` (Default: -1) | **No** | If provided and `Increase`/`Decrease` are not provided, set the current stage to the specified stage. If this value is less than 0, set the current stage to the final growth stage. If the specified stage does not exist, the block will be set to the closest extant stage. |
### FertilizeSoil
If the target block is unfertilized tilled soil or a farming block on top of unfertilized tilled soil, fertilizes the soil.
The interaction will fail otherwise.
**FertilizeSoil Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| RefreshModifiers | `Array` (Element Type: `String`) | **No** | Does nothing. |
### HarvestCrop
This interaction will attempt to execute the harvest action on the current block target and add the crop's drops to the User entity's
inventory. The block will either be destroyed or set to its post-harvest growth stage, depending on the farming
configuration of the block. This will happen (and the player will receive the full harvest drops) regardless of the
block's current growth stage. This interaction has no special failure conditions, even if the block target isn't a
farming block or gathering isn't permitted in the current world, but nothing will occur in those cases.
**HarvestCrop Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
### UseWateringCan
If the target block is tilled soil or a farming block on top of tilled soil, waters the soil for the specified duration.
The interaction will fail otherwise.
**UseWateringCan Fields**
| Field Name | Type | Required? | Notes |
| ---------------- | ------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
| UserLatestTarget | `Boolean` (Default: false) | **No** | If true, update this interaction chain's target block from the client's current cursor position before executing the interaction. |
| Duration | `Integer` (Default: 0) | **No** | The duration in seconds to water the soil for. |
| RefreshModifiers | `Array` (Element Type: `String`) | **No** | Does nothing. |
--- Events
Source: https://hytalemodding.dev/en/docs/server/events
## IEvent
* AddPlayerToWorldEvent
* AllNPCsLoadedEvent
* AllWorldsLoadedEvent
* AssetMonitorEvent
* AssetStoreMonitorEvent
* CommonAssetMonitorEvent
* AssetPackRegisterEvent
* AssetPackUnregisterEvent
* AssetStoreEvent
* RegisterAssetStoreEvent
* RemoveAssetStoreEvent
* AssetsEvent
* GenerateAssetsEvent
* LoadedAssetsEvent
* RemovedAssetsEvent
* BootEvent
* ChunkEvent
* ChunkPreLoadProcessEvent
* DrainPlayerFromWorldEvent
* EditorClientEvent
* AssetEditorActivateButtonEvent
* AssetEditorAssetCreatedEvent
* AssetEditorClientDisconnectEvent
* AssetEditorSelectAssetEvent
* AssetEditorUpdateWeatherPreviewLockEvent
* EntityEvent
* EntityRemoveEvent
* LivingEntityInventoryChangeEvent
* GenerateDefaultLanguageEvent
* GenerateSchemaEvent
* GenerateServerStateEvent
* ItemContainerChangeEvent
* ~~LivingEntityUseBlockEvent~~ DEPRECATED
* LoadAssetEvent
* LoadedNPCEvent
* MessagesUpdated
* PlayerConnectEvent
* PlayerEvent
* ~~PlayerCraftEvent~~ DEPRECATED
* ~~PlayerInteractEvent~~ DEPRECATED
* PlayerMouseButtonEvent
* PlayerMouseMotionEvent
* PlayerReadyEvent
* PlayerRefEvent
* PlayerDisconnectEvent
* PlayerSetupConnectEvent
* PlayerSetupDisconnectEvent
* PluginEvent
* PluginSetupEvent
* ~~PrepareUniverseEvent~~ DEPRECATED
* ShutdownEvent
* SingleplayerRequestAccessEvent
* TreasureChestOpeningEvent
* WindowCloseEvent
* WorldEvent
* AddWorldEvent
* RemoveWorldEvent
* StartWorldEvent
* WorldPathChangedEvent
## IAsyncEvent
* AssetEditorFetchAutoCompleteDataEvent
* AssetEditorRequestDataSetEvent
* PlayerChatEvent
* SendCommonAssetsEvent
## EcsEvent
* CancellableEcsEvent
* BreakBlockEvent
* ChangeGameModeEvent
* ChunkSaveEvent
* ChunkUnloadEvent
* CraftRecipeEvent
* Post
* Pre
* Damage
* DamageBlockEvent
* DropItemEvent
* Drop
* PlayerRequest
* InteractivelyPickupItemEvent
* PlaceBlockEvent
* PrefabPasteEvent
* SwitchActiveSlotEvent
* DiscoverInstanceEvent
* Display
* DiscoverZoneEvent
* Display
* MoonPhaseChangeEvent
* UseBlockEvent
* Post
* Pre
--- ArgTypes
Source: https://hytalemodding.dev/en/docs/server/argtypes
| Name | Target Class | Description | |
| :---------------------------- | :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | - |
| Boolean | java.lang.Boolean | A 'true' or 'false' input | |
| Integer | java.lang.Integer | A whole number | |
| String | java.lang.String | Words, text, numbers, letters. To input more than one word at a time, you may need to wrap it with double quotes (`"my fancy sentence"`). | |
| Float | java.lang.Float | A floating-pointer number | |
| Double | java.lang.Double | A decimal number | |
| UUID | java.util.UUID | A UUID (Universally Unique IDentifier) | |
| Player\_UUID | java.util.UUID | A UUID or an online player username | |
| Game\_Profile\_Lookup\_Async | ProfileServiceClient.PublicGameProfile | A player UUID or username. If not found locally, performs a remote lookup via the Profile Service. | |
| Relative\_Double\_Coord | Coord | An x, y, or z coordinate as a decimal, optionally relative by specifying a tilde (\~) in front of the number | |
| Relative\_Int\_Coord | IntCoord | An x, y, or z coordinate as a whole number, optionally relative by specifying a tilde (\~) in front of the number | |
| Relative\_Integer | RelativeInteger | A tilde to mark an integer relative to a base | |
| Relative\_Float | RelativeFloat | A tilde to mark a float relative to a base | |
| Player\_Ref | PlayerRef | A UUID or an online player's username | |
| World | World | A world folder name | |
| Model\_Asset | ModelAsset | A reference to an Asset of type Model | |
| Weather\_Asset | Weather | A reference to an Asset of type Weather | |
| Interaction\_Asset | Interaction | A reference to an Asset of type Interaction | |
| Root\_Interaction\_Asset | RootInteraction | A reference to an Asset of type Root Interaction | |
| Effect\_Asset | EntityEffect | A refernece to an Asset of type EntityEffect | |
| Environment\_Asset | Environment | A reference to an Asset of type Environment | |
| Item\_Asset | Item | A reference to an Asset of type Item | |
| Block\_Type\_Asset | BlockType | A reference to an Asset of type BlockType | |
| Particle\_System | ParticleSystem | A reference to an Asset of type ParticleSystem | |
| Hitbox\_Collision\_Config | HitboxCollisionConfig | A reference to an Asset of type HitboxCollisionConfig | |
| Repulsion\_Config | RepulsionConfig | A reference to an Asset of type RepulsionConfig | |
| Sound\_Event\_Asset | SoundEvent | A reference to an Asset of type SoundEvent | |
| Ambiance\_Fx\_Asset | AmbianceFX | A reference to an Asset of type AmbianceFX | |
| Sound\_Category | SoundCategory | A sound category (sfx, music, ambient, ui) | |
| Entity\_ID | `ArgWrapper` | A UUID representing an entity id | |
| Integer\_Comparison\_Operator | IntegerComparisonOperator | A mathematical sign for integer comparison | |
| Integer\_Operation | IntegerOperation | A mathematical sign for performing an operation | |
| Int\_Range | `Pair` | Two integers representing a minimum and maximum of a range | |
| Relative\_Int\_Range | RelativeIntRange | Two integers representing a minimum and maximum of a range | |
| Vector2I | Vector2i | Two integers, generally corresponding to x/z axis | |
| Vector3I | Vector3i | Three integers, generally corresponding to x/y/z axis | |
| Relative\_Vector3I | RelativeVector3i | Three optionally relative integers, generally corresponding to x/y/z axis | |
| Relative\_Block\_Position | RelativeIntPosition | A position with three integer coordinates, representing a block's location in world space. | |
| Relative\_Position | Relative | A position with three decimal coordinates, representing a location in world space. | |
| Relative\_Chunk\_Position | RealtiveChunkPosition | A position with two integer coordinates (x, z), representing a chunk's location. | |
| Rotation | Vector3f | A rotation with three coordinates, representing the pitch, yaw, and roll respectively | |
| Block\_Type\_Key | String | A block type | |
| Block\_ID | Integer | A block type, converted to an int id | |
| Color | Integer | A color value in hex format, hex integer, or decimal integer | |
| Weighted\_Block\_Type | `Pair` | A weight corresponding to a blocktype | |
| Weighted\_Block\_Entry | String | A block with optional weight prefix | |
| Block\_Pattern | BlockPattern | A list of blocks with optional weights | |
| Individual\_Block\_Mask | BlockMask | Create a block mask using symbols and block names | |
| Block\_Mask | BlockMask | A tick rate value (e.g., 30tps, 33ms, or 30) | |
| Tick\_Rate | Integer | A list of block masks that combine together | |
| Game\_Mode | GameMode | GameMode | |
--- Sounds
Source: https://hytalemodding.dev/en/docs/server/sounds
This page contains a list of all Sounds available to be used as a key in the AssetMap.
* SFX\_Alchemy\_Bench\_Close
* SFX\_Alchemy\_Bench\_Craft
* SFX\_Alchemy\_Bench\_Open
* SFX\_Antelope\_Alerted
* SFX\_Antelope\_Death
* SFX\_Antelope\_Hurt
* SFX\_Antelope\_Run
* SFX\_Antelope\_Walk
* SFX\_Arcane\_Workbench\_Close\_Local
* SFX\_Arcane\_Workbench\_Craft
* SFX\_Arcane\_Workbench\_Open\_Local
* SFX\_Armour\_Bench\_Close
* SFX\_Armour\_Bench\_Craft
* SFX\_Armour\_Bench\_Open
* SFX\_Arrow\_Fire\_Hit
* SFX\_Arrow\_Fire\_Miss
* SFX\_Arrow\_Frost\_Hit
* SFX\_Arrow\_Frost\_Miss
* SFX\_Arrow\_FullCharge\_Hit
* SFX\_Arrow\_FullCharge\_Miss
* SFX\_Arrow\_HalfCharge\_Hit
* SFX\_Arrow\_HalfCharge\_Miss
* SFX\_Arrow\_NoCharge\_Hit
* SFX\_Arrow\_NoCharge\_Miss
* SFX\_Arrow\_Whistle
* SFX\_Attn\_Loud
* SFX\_Attn\_Moderate
* SFX\_Attn\_Quiet
* SFX\_Attn\_VeryLoud
* SFX\_Attn\_VeryQuiet
* SFX\_Avatar\_Powers\_Disable
* SFX\_Avatar\_Powers\_Disable\_Local
* SFX\_Avatar\_Powers\_Enable
* SFX\_Avatar\_Powers\_Enable\_Local
* SFX\_Axe\_Crude\_Impact
* SFX\_Axe\_Crude\_Swing
* SFX\_Axe\_Iron\_Impact
* SFX\_Axe\_Iron\_Swing
* SFX\_Axe\_Special\_Impact
* SFX\_Axe\_Special\_Swing
* SFX\_Axe\_Stone\_Trork\_Hit
* SFX\_Axe\_Stone\_Trork\_Miss
* SFX\_Bat\_Alerted
* SFX\_Bat\_Death
* SFX\_Bat\_Hurt
* SFX\_Battleaxe\_T1\_Block\_Impact
* SFX\_Battleaxe\_T1\_Impact
* SFX\_Battleaxe\_T1\_Launch
* SFX\_Battleaxe\_T1\_Launch\_Local
* SFX\_Battleaxe\_T1\_Raise
* SFX\_Battleaxe\_T1\_Raise\_Local
* SFX\_Battleaxe\_T1\_Shove
* SFX\_Battleaxe\_T1\_Shove\_Local
* SFX\_Battleaxe\_T1\_Swing
* SFX\_Battleaxe\_T1\_Swing\_Charged
* SFX\_Battleaxe\_T1\_Swing\_Charged\_Local
* SFX\_Battleaxe\_T1\_Swing\_Down\_Local
* SFX\_Battleaxe\_T1\_Swing\_LR\_Local
* SFX\_Battleaxe\_T1\_Swing\_RL\_Local
* SFX\_Battleaxe\_T2\_Impact
* SFX\_Battleaxe\_T2\_Raise
* SFX\_Battleaxe\_T2\_Raise\_Local
* SFX\_Battleaxe\_T2\_Signature\_End
* SFX\_Battleaxe\_T2\_Signature\_End\_Local
* SFX\_Battleaxe\_T2\_Signature\_Swing
* SFX\_Battleaxe\_T2\_Signature\_Swing\_Local
* SFX\_Battleaxe\_T2\_Swing
* SFX\_Battleaxe\_T2\_Swing\_Charged
* SFX\_Battleaxe\_T2\_Swing\_Charged\_Local
* SFX\_Battleaxe\_T2\_Swing\_Down\_Local
* SFX\_Battleaxe\_T2\_Swing\_LR\_Local
* SFX\_Battleaxe\_T2\_Swing\_RL\_Local
* SFX\_Bear\_Grizzly\_Alerted
* SFX\_Bear\_Grizzly\_Attack
* SFX\_Bear\_Grizzly\_Death
* SFX\_Bear\_Grizzly\_Hurt
* SFX\_Bear\_Grizzly\_Run
* SFX\_Bear\_Grizzly\_Sleep
* SFX\_Bear\_Walk
* SFX\_Bench\_Placeholder
* SFX\_Bison\_Alerted
* SFX\_Bison\_Death
* SFX\_Bison\_Hurt
* SFX\_Bison\_Idle
* SFX\_Bison\_Run
* SFX\_Bison\_Walk
* SFX\_Blunderbuss\_Bullet\_WhizBy
* SFX\_Blunderbuss\_Fire
* SFX\_Blunderbuss\_Fire\_Local
* SFX\_Blunderbuss\_Hit
* SFX\_Blunderbuss\_Load
* SFX\_Blunderbuss\_Load\_Local
* SFX\_Blunderbuss\_Miss
* SFX\_Blunderbuss\_No\_Ammo
* SFX\_Boar\_Alerted
* SFX\_Boar\_Death
* SFX\_Boar\_Hurt
* SFX\_Boar\_Run
* SFX\_Boar\_Sleep
* SFX\_Boar\_Walk
* SFX\_Bomb\_Fire\_Goblin\_Bounce
* SFX\_Bomb\_Fire\_Goblin\_Death
* SFX\_Bomb\_Fire\_Goblin\_Hit
* SFX\_Bomb\_Fire\_Goblin\_Miss
* SFX\_Bomb\_Fuse
* SFX\_Bone\_Break
* SFX\_Bone\_Build
* SFX\_Bone\_Hit
* SFX\_Bone\_Land
* SFX\_Bone\_Walk
* SFX\_Bow\_No\_Ammo
* SFX\_Bow\_T1\_Block\_Impact
* SFX\_Bow\_T1\_Draw
* SFX\_Bow\_T1\_Draw\_Local
* SFX\_Bow\_T1\_Raise
* SFX\_Bow\_T1\_Raise\_Local
* SFX\_Bow\_T1\_Shoot
* SFX\_Bow\_T1\_Shoot\_Local
* SFX\_Bow\_T1\_Swing
* SFX\_Bow\_T1\_Swing\_Local
* SFX\_Bow\_T2\_Draw
* SFX\_Bow\_T2\_Draw\_Local
* SFX\_Bow\_T2\_Shoot
* SFX\_Bow\_T2\_Shoot\_Local
* SFX\_Bow\_T2\_Signature\_Loop
* SFX\_Bow\_T2\_Signature\_Loop\_Local
* SFX\_Bow\_T2\_Signature\_Nock
* SFX\_Bow\_T2\_Signature\_Nock\_Local
* SFX\_Bow\_T2\_Signature\_Shoot
* SFX\_Bow\_T2\_Signature\_Shoot\_Local
* SFX\_Bramble\_MoveIn
* SFX\_Branch\_Break
* SFX\_Branch\_Build
* SFX\_Branch\_Hit
* SFX\_Branch\_Land
* SFX\_Branch\_Walk
* SFX\_Brazier\_Break
* SFX\_Brazier\_Build
* SFX\_Bunny\_Alerted
* SFX\_Bunny\_Death
* SFX\_Bunny\_Hurt
* SFX\_Bush\_Break
* SFX\_Bush\_Hit
* SFX\_Bush\_MoveIn
* SFX\_Cactus\_Break
* SFX\_Cactus\_Hit
* SFX\_Cactus\_Large\_Hit
* SFX\_Cactus\_Small\_Break
* SFX\_Cactus\_Small\_Hit
* SFX\_Calf\_Hurt
* SFX\_Calf\_Run
* SFX\_Calf\_Walk
* SFX\_Camel\_Alerted
* SFX\_Camel\_Death
* SFX\_Camel\_Hurt
* SFX\_Camel\_Laydown
* SFX\_Camel\_Run
* SFX\_Camel\_Sleep
* SFX\_Camel\_Wake
* SFX\_Camel\_Walk
* SFX\_Campfire\_Break
* SFX\_Campfire\_Build
* SFX\_Campfire\_Close\_Local
* SFX\_Campfire\_Default\_Loop
* SFX\_Campfire\_Open\_Local
* SFX\_Campfire\_Processing
* SFX\_Campfire\_Processing\_End
* SFX\_Campfire\_Processing\_Failed
* SFX\_Candle\_Default\_Loop
* SFX\_Candle\_Off
* SFX\_Capture\_Crate\_Capture\_Fail\_Local
* SFX\_Capture\_Crate\_Capture\_Succeed
* SFX\_Capture\_Crate\_Capture\_Succeed\_Local
* SFX\_Capture\_Crate\_Spawn\_Fail\_Local
* SFX\_Capture\_Crate\_Spawn\_Succeed
* SFX\_Cauldron\_Bubbling
* SFX\_Cauldron\_Bubbling\_Small
* SFX\_Chest\_Legendary\_Close\_Player
* SFX\_Chest\_Legendary\_FirstOpen\_Player
* SFX\_Chest\_Legendary\_Loop
* SFX\_Chest\_Legendary\_Open
* SFX\_Chest\_Wooden\_Close
* SFX\_Chest\_Wooden\_Open
* SFX\_Chest\_Wooden\_Open\_Player
* SFX\_Chick\_Alerted
* SFX\_Chick\_Death
* SFX\_Chick\_Hurt
* SFX\_Chicken\_Alerted
* SFX\_Chicken\_Death
* SFX\_Chicken\_Flee
* SFX\_Chicken\_Hurt
* SFX\_Chicken\_Run
* SFX\_Chicken\_Walk
* SFX\_Clay\_Pot\_Large\_Break
* SFX\_Clay\_Pot\_Large\_Build
* SFX\_Clay\_Pot\_Large\_Hit
* SFX\_Clay\_Pot\_Large\_Walk
* SFX\_Clay\_Pot\_Small\_Break
* SFX\_Clay\_Pot\_Small\_Build
* SFX\_Clay\_Pot\_Small\_Hit
* SFX\_Clay\_Pot\_Small\_Walk
* SFX\_Cloth\_Break
* SFX\_Cloth\_Build
* SFX\_Cloth\_Hit
* SFX\_Cloth\_Land
* SFX\_Cloth\_Walk
* SFX\_Club\_Meat\_Impact
* SFX\_Club\_Meat\_Swing
* SFX\_Club\_Special\_Impact
* SFX\_Club\_Special\_Swing
* SFX\_Club\_Steel\_Impact
* SFX\_Club\_Steel\_Swing
* SFX\_Club\_Wood\_Impact
* SFX\_Club\_Wood\_Swing
* SFX\_Cocoon\_Active
* SFX\_Cocoon\_Break
* SFX\_Cocoon\_Build
* SFX\_Cocoon\_Hit
* SFX\_Cocoon\_Walk
* SFX\_Coins\_Land
* SFX\_Coins\_Walk
* SFX\_Consume\_Bread
* SFX\_Consume\_Bread\_Local
* SFX\_Cow\_Alerted
* SFX\_Cow\_Death
* SFX\_Cow\_Hurt
* SFX\_Cow\_Idle
* SFX\_Cow\_Run
* SFX\_Cow\_Sleep
* SFX\_Cow\_Walk
* SFX\_Crawler\_Void\_Alerted
* SFX\_Crawler\_Void\_Alerted\_02
* SFX\_Crawler\_Void\_Death
* SFX\_Crawler\_Void\_Despawn
* SFX\_Crawler\_Void\_Hurt
* SFX\_Crawler\_Void\_Run
* SFX\_Crawler\_Void\_Sleep
* SFX\_Crawler\_Void\_Spawn
* SFX\_Crawler\_Void\_Spawn\_02
* SFX\_Crawler\_Void\_Walk
* SFX\_Creative\_Play\_Add\_Mask
* SFX\_Creative\_Play\_Brush\_Erase
* SFX\_Creative\_Play\_Brush\_Mode
* SFX\_Creative\_Play\_Brush\_Paint\_Base
* SFX\_Creative\_Play\_Brush\_Paint\_Idle\_Layer
* SFX\_Creative\_Play\_Brush\_Paint\_Move\_Layer
* SFX\_Creative\_Play\_Brush\_Shape
* SFX\_Creative\_Play\_Brush\_Stamp
* SFX\_Creative\_Play\_Error
* SFX\_Creative\_Play\_Eyedropper\_Select
* SFX\_Creative\_Play\_Paste
* SFX\_Creative\_Play\_Selection\_Drag
* SFX\_Creative\_Play\_Selection\_Place
* SFX\_Creative\_Play\_Selection\_Scale
* SFX\_Creative\_Play\_Selection\_Widget
* SFX\_Creative\_Play\_Set\_Mask
* SFX\_Crocodile\_Alerted
* SFX\_Crocodile\_Death
* SFX\_Crocodile\_Hurt
* SFX\_Crops\_Grow
* SFX\_Crops\_Grow\_Stage\_Complete
* SFX\_Crow\_Death
* SFX\_Crow\_Hurt
* SFX\_Crystal\_Break
* SFX\_Crystal\_Build
* SFX\_Crystal\_Hit
* SFX\_Crystal\_Walk
* SFX\_Daggers\_T1\_Guard
* SFX\_Daggers\_T1\_Guard\_Local
* SFX\_Daggers\_T1\_Pounce
* SFX\_Daggers\_T1\_Pounce\_Local
* SFX\_Daggers\_T1\_Slash\_Impact
* SFX\_Daggers\_T1\_Stab\_Double\_Impact
* SFX\_Daggers\_T1\_Stab\_Impact
* SFX\_Daggers\_T1\_Stab\_Left\_Local
* SFX\_Daggers\_T1\_Stab\_Retreat
* SFX\_Daggers\_T1\_Stab\_Retreat\_Local
* SFX\_Daggers\_T1\_Stab\_Right\_Local
* SFX\_Daggers\_T1\_Swing
* SFX\_Daggers\_T1\_Swing\_Double
* SFX\_Daggers\_T1\_Swing\_Double\_Local
* SFX\_Daggers\_T1\_Swing\_LR\_Local
* SFX\_Daggers\_T1\_Swing\_RL\_Local
* SFX\_Daggers\_T2\_Guard
* SFX\_Daggers\_T2\_Guard\_Local
* SFX\_Daggers\_T2\_Signature\_P1
* SFX\_Daggers\_T2\_Signature\_P1\_Local
* SFX\_Daggers\_T2\_Signature\_P2
* SFX\_Daggers\_T2\_Signature\_P2\_Local
* SFX\_Daggers\_T2\_Signature\_P3
* SFX\_Daggers\_T2\_Signature\_P3\_Local
* SFX\_Daggers\_T2\_Slash\_Impact
* SFX\_Daggers\_T2\_Stab\_Double\_Impact
* SFX\_Daggers\_T2\_Stab\_Impact
* SFX\_Daggers\_T2\_Stab\_Left\_Local
* SFX\_Daggers\_T2\_Stab\_Retreat
* SFX\_Daggers\_T2\_Stab\_Retreat\_Local
* SFX\_Daggers\_T2\_Stab\_Right\_Local
* SFX\_Daggers\_T2\_Swing
* SFX\_Daggers\_T2\_Swing\_Double
* SFX\_Daggers\_T2\_Swing\_Double\_Local
* SFX\_Daggers\_T2\_Swing\_LR\_Local
* SFX\_Daggers\_T2\_Swing\_RL\_Local
* SFX\_Deer\_Doe\_Alerted
* SFX\_Deer\_Doe\_Death
* SFX\_Deer\_Doe\_Hurt
* SFX\_Deer\_Doe\_Run
* SFX\_Deer\_Doe\_Sleep
* SFX\_Deer\_Stag\_Alerted
* SFX\_Deer\_Stag\_Death
* SFX\_Deer\_Stag\_Hurt
* SFX\_Deer\_Stag\_Roar
* SFX\_Deer\_Stag\_Run
* SFX\_Deer\_Stag\_Sleep
* SFX\_Deer\_Walk
* SFX\_Default\_Break
* SFX\_Default\_Build
* SFX\_Default\_Clone
* SFX\_Default\_Harvest
* SFX\_Default\_Walk
* SFX\_Deployable\_Totem\_Heal\_Despawn
* SFX\_Deployable\_Totem\_Heal\_Effect\_Local
* SFX\_Deployable\_Totem\_Heal\_Spawn
* SFX\_Deployable\_Totem\_Slowing\_Despawn
* SFX\_Deployable\_Totem\_Slowing\_Effect\_Local
* SFX\_Deployable\_Totem\_Slowing\_Spawn
* SFX\_Dirt\_Break
* SFX\_Dirt\_Build
* SFX\_Dirt\_Clone
* SFX\_Dirt\_Hit
* SFX\_Dirt\_Land
* SFX\_Dirt\_Walk
* SFX\_Discovery\_Z1\_Medium
* SFX\_Discovery\_Z1\_Short
* SFX\_Discovery\_Z2\_Medium
* SFX\_Discovery\_Z2\_Short
* SFX\_Discovery\_Z3\_Medium
* SFX\_Discovery\_Z3\_Short
* SFX\_Discovery\_Z4\_Medium
* SFX\_Discovery\_Z4\_Short
* SFX\_Divine\_Respawn
* SFX\_Door\_Ancient\_Close
* SFX\_Door\_Ancient\_Open
* SFX\_Door\_Crude\_Close
* SFX\_Door\_Crude\_Open
* SFX\_Door\_Desert\_Close
* SFX\_Door\_Desert\_Open
* SFX\_Door\_Jungle\_Close
* SFX\_Door\_Jungle\_Open
* SFX\_Door\_Lumberjack\_Close
* SFX\_Door\_Lumberjack\_Open
* SFX\_Door\_Temple\_Dark\_Close
* SFX\_Door\_Temple\_Dark\_Open
* SFX\_Door\_Temple\_Light\_Close
* SFX\_Door\_Temple\_Light\_Open
* SFX\_Door\_Wooden\_Close
* SFX\_Door\_Wooden\_Open
* SFX\_Drag\_Armor\_Cloth
* SFX\_Drag\_Armor\_Heavy
* SFX\_Drag\_Armor\_Leather
* SFX\_Drag\_Blocks\_Gravel
* SFX\_Drag\_Blocks\_Soft
* SFX\_Drag\_Blocks\_Splatty
* SFX\_Drag\_Blocks\_Stone
* SFX\_Drag\_Blocks\_Wood
* SFX\_Drag\_Item\_Default
* SFX\_Drag\_Items\_Bones
* SFX\_Drag\_Items\_Chest
* SFX\_Drag\_Items\_Clay
* SFX\_Drag\_Items\_Cloth
* SFX\_Drag\_Items\_Foliage
* SFX\_Drag\_Items\_Gadget
* SFX\_Drag\_Items\_Gems
* SFX\_Drag\_Items\_Ingots
* SFX\_Drag\_Items\_Leather
* SFX\_Drag\_Items\_Metal
* SFX\_Drag\_Items\_Paper
* SFX\_Drag\_Items\_Potion
* SFX\_Drag\_Items\_Seeds
* SFX\_Drag\_Items\_Shells
* SFX\_Drag\_Items\_Splatty
* SFX\_Drag\_Weapon\_Blade\_Small
* SFX\_Drag\_Weapon\_Blunt\_Large
* SFX\_Drag\_Weapons\_Arrows
* SFX\_Drag\_Weapons\_Blade\_Large
* SFX\_Drag\_Weapons\_Blunt\_Small
* SFX\_Drag\_Weapons\_Books
* SFX\_Drag\_Weapons\_Shield\_Metal
* SFX\_Drag\_Weapons\_Shield\_Wood
* SFX\_Drag\_Weapons\_Stone\_Large
* SFX\_Drag\_Weapons\_Stone\_Small
* SFX\_Drag\_Weapons\_Wand
* SFX\_Drag\_Weapons\_Wood
* SFX\_Dragon\_Sleep
* SFX\_Drop\_Armor\_Cloth
* SFX\_Drop\_Armor\_Heavy
* SFX\_Drop\_Armor\_Leather
* SFX\_Drop\_Blocks\_Gravel
* SFX\_Drop\_Blocks\_Soft
* SFX\_Drop\_Blocks\_Splatty
* SFX\_Drop\_Blocks\_Stone
* SFX\_Drop\_Blocks\_Wood
* SFX\_Drop\_Item\_Default
* SFX\_Drop\_Items\_Bones
* SFX\_Drop\_Items\_Chest
* SFX\_Drop\_Items\_Clay
* SFX\_Drop\_Items\_Cloth
* SFX\_Drop\_Items\_Foliage
* SFX\_Drop\_Items\_Gadget
* SFX\_Drop\_Items\_Gems
* SFX\_Drop\_Items\_Ingots
* SFX\_Drop\_Items\_Leather
* SFX\_Drop\_Items\_Metal
* SFX\_Drop\_Items\_Paper
* SFX\_Drop\_Items\_Potion
* SFX\_Drop\_Items\_Seeds
* SFX\_Drop\_Items\_Shells
* SFX\_Drop\_Items\_Splatty
* SFX\_Drop\_Weapon\_Blade\_Small
* SFX\_Drop\_Weapon\_Blunt\_Large
* SFX\_Drop\_Weapons\_Arrows
* SFX\_Drop\_Weapons\_Blade\_Large
* SFX\_Drop\_Weapons\_Blunt\_Small
* SFX\_Drop\_Weapons\_Books
* SFX\_Drop\_Weapons\_Shield\_Metal
* SFX\_Drop\_Weapons\_Shield\_Wood
* SFX\_Drop\_Weapons\_Stone\_Large
* SFX\_Drop\_Weapons\_Stone\_Small
* SFX\_Drop\_Weapons\_Wand
* SFX\_Drop\_Weapons\_Wood
* SFX\_Duck\_Alerted
* SFX\_Duck\_Death
* SFX\_Duck\_Hurt
* SFX\_Duck\_Run
* SFX\_Effect\_Burn\_Local
* SFX\_Effect\_Burn\_World
* SFX\_Effect\_Poison\_Local
* SFX\_Effect\_Poison\_World
* SFX\_Egg\_Hit
* SFX\_Egg\_Miss
* SFX\_Eggsac\_Active
* SFX\_Emberwulf\_Alerted
* SFX\_Emberwulf\_Attack\_Bite
* SFX\_Emberwulf\_Death
* SFX\_Emberwulf\_Hurt
* SFX\_Emberwulf\_Run
* SFX\_Emberwulf\_Sleep
* SFX\_Emberwulf\_Walk
* SFX\_Emit\_Forgotten\_Whispers
* SFX\_Emit\_Lake\_Water
* SFX\_Emit\_Temple\_Wisps
* SFX\_Emit\_Tree\_Creak
* SFX\_Emit\_Wind\_Grass
* SFX\_Emit\_Wind\_Gusts
* SFX\_Env\_Emit\_Fluid\_Lava
* SFX\_Env\_Emit\_Fluid\_Water
* SFX\_Env\_Emit\_Fluid\_Water\_Far
* SFX\_Env\_Emit\_Geyzer
* SFX\_Eye\_Void\_Alerted
* SFX\_Eye\_Void\_Attack\_Blast
* SFX\_Eye\_Void\_Attack\_Summon
* SFX\_Eye\_Void\_Death
* SFX\_Eye\_Void\_Fly\_Movement
* SFX\_Eye\_Void\_Hurt
* SFX\_Eye\_Void\_Idle
* SFX\_Fen\_Stalker\_Alerted
* SFX\_Fen\_Stalker\_Attack\_Swing
* SFX\_Fen\_Stalker\_Attack\_Swipe
* SFX\_Fen\_Stalker\_Death
* SFX\_Fen\_Stalker\_Eat
* SFX\_Fen\_Stalker\_Eat\_Finish
* SFX\_Fen\_Stalker\_Greet
* SFX\_Fen\_Stalker\_Hurt
* SFX\_Fen\_Stalker\_Run
* SFX\_Fen\_Stalker\_Scared
* SFX\_Fen\_Stalker\_Seek
* SFX\_Fen\_Stalker\_Sniff
* SFX\_Feran\_Death
* SFX\_Fern\_Break
* SFX\_Fern\_MoveIn
* SFX\_Fireball\_Bounce
* SFX\_Fireball\_Death
* SFX\_Fireball\_Miss
* SFX\_Fish\_Death
* SFX\_Fish\_Flee
* SFX\_Fish\_Hurt
* SFX\_Flail\_Charge
* SFX\_Flail\_Charge\_Local
* SFX\_Flail\_Swing
* SFX\_Flail\_Swing\_Left\_Local
* SFX\_Flail\_Swing\_Right\_Local
* SFX\_Flame\_Break
* SFX\_Flame\_Build
* SFX\_Flame\_Default\_Loop
* SFX\_Flamingo\_Alerted
* SFX\_Flamingo\_Death
* SFX\_Flamingo\_Fly
* SFX\_Flamingo\_Hurt
* SFX\_Forgotten\_Temple\_Emit\_Birds
* SFX\_Forgotten\_Temple\_Emit\_Birds\_Interior
* SFX\_Fox\_Alerted
* SFX\_Fox\_Death
* SFX\_Fox\_Hurt
* SFX\_Fox\_Run
* SFX\_Fox\_Sleep
* SFX\_Frog\_Alerted
* SFX\_Frog\_Croak
* SFX\_Frog\_Death
* SFX\_Frog\_Hurt
* SFX\_Frog\_Idle
* SFX\_Frog\_Run
* SFX\_Furnace\_Bench\_Close
* SFX\_Furnace\_Bench\_Open
* SFX\_Furnace\_Bench\_Processing
* SFX\_Furnace\_Bench\_Processing\_Complete
* SFX\_Furnace\_Bench\_Processing\_End
* SFX\_Furnace\_Bench\_Processing\_Failed
* SFX\_Gecko\_Alerted
* SFX\_Gecko\_Death
* SFX\_Gecko\_Hurt
* SFX\_Gem\_Break
* SFX\_Gem\_Emit\_Loop
* SFX\_Generic\_Crafting\_Failed
* SFX\_Glass\_Break
* SFX\_Global\_Weather\_Thunder
* SFX\_Goat\_Run
* SFX\_Goat\_Walk
* SFX\_Goblin\_Alerted
* SFX\_Goblin\_Death
* SFX\_Goblin\_Hurt
* SFX\_Goblin\_Lobber\_Bomb\_Bounce
* SFX\_Goblin\_Lobber\_Bomb\_Death
* SFX\_Goblin\_Lobber\_Bomb\_Hit
* SFX\_Goblin\_Lobber\_Bomb\_Miss
* SFX\_Goblin\_Run
* SFX\_Goblin\_Search
* SFX\_Golem\_Earth\_Alerted
* SFX\_Golem\_Earth\_Death
* SFX\_Golem\_Earth\_Hurt
* SFX\_Golem\_Earth\_Laydown
* SFX\_Golem\_Earth\_Slam
* SFX\_Golem\_Earth\_Slam\_Impact
* SFX\_Golem\_Earth\_Spin
* SFX\_Golem\_Earth\_Stomp
* SFX\_Golem\_Earth\_Stomp\_Impact
* SFX\_Golem\_Earth\_Swing
* SFX\_Golem\_Earth\_Swing\_Impact
* SFX\_Golem\_Earth\_Wake
* SFX\_Golem\_Firesteel\_Alerted\_01
* SFX\_Golem\_Firesteel\_Alerted\_02
* SFX\_Golem\_Firesteel\_Death
* SFX\_Golem\_Firesteel\_Laydown
* SFX\_Golem\_Firesteel\_Wake
* SFX\_Golem\_Frost\_Alerted
* SFX\_Golem\_Frost\_Death
* SFX\_Golem\_Frost\_Hurt
* SFX\_Golem\_Frost\_Laydown
* SFX\_Golem\_Frost\_Slam
* SFX\_Golem\_Frost\_Slam\_Impact
* SFX\_Golem\_Frost\_Spin
* SFX\_Golem\_Frost\_Stomp
* SFX\_Golem\_Frost\_Swing
* SFX\_Golem\_Frost\_Swing\_Impact
* SFX\_Golem\_Frost\_Wake
* SFX\_Golem\_Sand\_Alerted
* SFX\_Golem\_Sand\_Death
* SFX\_Golem\_Sand\_Hurt
* SFX\_Golem\_Sand\_Laydown
* SFX\_Golem\_Sand\_Slam
* SFX\_Golem\_Sand\_Slam\_Impact
* SFX\_Golem\_Sand\_Spin
* SFX\_Golem\_Sand\_Stomp
* SFX\_Golem\_Sand\_Stomp\_Impact
* SFX\_Golem\_Sand\_Swing
* SFX\_Golem\_Sand\_Swing\_Impact
* SFX\_Golem\_Sand\_Wake
* SFX\_Grass\_Break
* SFX\_Grass\_Build
* SFX\_Grass\_Hit
* SFX\_Grass\_Land
* SFX\_Grass\_Walk
* SFX\_Gravel\_Break
* SFX\_Gravel\_Build
* SFX\_Gravel\_Hit
* SFX\_Gravel\_Land
* SFX\_Gravel\_Walk
* SFX\_GunPvP\_Assault\_Rifle\_Bullet\_Death
* SFX\_GunPvP\_Grenade\_Frag\_Bounce
* SFX\_GunPvP\_Grenade\_Frag\_Death
* SFX\_GunPvP\_Grenade\_Frag\_Hit
* SFX\_GunPvP\_Grenade\_Frag\_Miss
* SFX\_GunPvP\_Handgun\_Bullet\_Death
* SFX\_Gun\_Fire
* SFX\_Hand\_Crossbow\_T1\_Block\_Impact
* SFX\_Hand\_Crossbow\_T1\_Raise
* SFX\_Hand\_Crossbow\_T1\_Raise\_Local
* SFX\_Hand\_Crossbow\_T1\_Shove
* SFX\_Hand\_Crossbow\_T1\_Shove\_Local
* SFX\_Hand\_Crossbow\_T2\_Load
* SFX\_Hand\_Crossbow\_T2\_Load\_Local
* SFX\_Hand\_Crossbow\_T2\_Reload\_Start
* SFX\_Hand\_Crossbow\_T2\_Reload\_Start\_Local
* SFX\_Handgun\_Fire
* SFX\_Handgun\_Fire\_Local
* SFX\_Hatchet\_T1\_Swing\_RL\_Local
* SFX\_Hatchet\_T2\_Impact\_Nice
* SFX\_Health\_Potion\_High\_Drink
* SFX\_Health\_Potion\_High\_Drink\_Local
* SFX\_Health\_Potion\_Low\_Drink
* SFX\_Health\_Potion\_Low\_Drink\_Local
* SFX\_Hedera\_Scream
* SFX\_Hoe\_T1\_Swing\_Down\_Local
* SFX\_Hoe\_T1\_Till
* SFX\_Horse\_Alerted
* SFX\_Horse\_Death
* SFX\_Horse\_Hurt
* SFX\_Horse\_Idle
* SFX\_Horse\_Sleep
* SFX\_Horse\_Wake
* SFX\_Hyena\_Alerted
* SFX\_Hyena\_Death
* SFX\_Hyena\_Hurt
* SFX\_Hyena\_Idle
* SFX\_Ice\_Ball\_Death
* SFX\_Ice\_Bolt\_Death
* SFX\_Ice\_Break
* SFX\_Ice\_Build
* SFX\_Ice\_Hit
* SFX\_Ice\_Item\_Impact
* SFX\_Ice\_Item\_Swing
* SFX\_Ice\_Land
* SFX\_Ice\_Walk
* SFX\_Incorrect\_Tool
* SFX\_Item\_Break
* SFX\_Item\_Repair
* SFX\_Klops\_Alerted
* SFX\_Klops\_Death
* SFX\_Klops\_Hurt
* SFX\_Klops\_Idle
* SFX\_Klops\_Run
* SFX\_Kweebec\_Plushie\_Impact
* SFX\_Lamb\_Alerted
* SFX\_Lamb\_Death
* SFX\_Lamb\_Hurt
* SFX\_Larva\_Alerted
* SFX\_Larva\_Death
* SFX\_Larva\_Despawn
* SFX\_Larva\_Hurt
* SFX\_Larva\_Spawn
* SFX\_LeavesGround\_Break
* SFX\_LeavesGround\_Hit
* SFX\_LeavesGround\_Land
* SFX\_LeavesGround\_Walk
* SFX\_Leaves\_Break
* SFX\_Leaves\_Hit
* SFX\_Leaves\_Walk
* SFX\_Leopard\_Snow\_Alerted
* SFX\_Leopard\_Snow\_Death
* SFX\_Leopard\_Snow\_Hurt
* SFX\_Leopard\_Snow\_Run
* SFX\_Light\_Melee\_T1\_Block
* SFX\_Light\_Melee\_T1\_Guard\_Hit
* SFX\_Light\_Melee\_T1\_Impact
* SFX\_Light\_Melee\_T1\_Lunge
* SFX\_Light\_Melee\_T1\_Lunge\_Charge
* SFX\_Light\_Melee\_T1\_Shove
* SFX\_Light\_Melee\_T1\_Swing
* SFX\_Light\_Melee\_T2\_Block
* SFX\_Light\_Melee\_T2\_Guard\_Break
* SFX\_Light\_Melee\_T2\_Guard\_Hit
* SFX\_Light\_Melee\_T2\_Lunge
* SFX\_Light\_Melee\_T2\_Lunge\_Charge
* SFX\_Light\_Melee\_T2\_Swing
* SFX\_Longsword\_Special\_Impact
* SFX\_Longsword\_Special\_Swing
* SFX\_Longsword\_Steel\_Charged\_Swing
* SFX\_Longsword\_Steel\_Impact
* SFX\_Longsword\_Steel\_Swing
* SFX\_Lumbermill\_Bench\_Close
* SFX\_Lumbermill\_Bench\_Open
* SFX\_Lumbermill\_Bench\_Processing
* SFX\_Mace\_T1\_Block\_Impact
* SFX\_Mace\_T1\_Impact
* SFX\_Mace\_T1\_Raise
* SFX\_Mace\_T1\_Raise\_Local
* SFX\_Mace\_T1\_Shove
* SFX\_Mace\_T1\_Shove\_Local
* SFX\_Mace\_T1\_Swing
* SFX\_Mace\_T1\_Swing\_Charged
* SFX\_Mace\_T1\_Swing\_Charged\_LR\_Local
* SFX\_Mace\_T1\_Swing\_Charged\_RL\_Local
* SFX\_Mace\_T1\_Swing\_Charged\_Up\_Local
* SFX\_Mace\_T1\_Swing\_LR\_Local
* SFX\_Mace\_T1\_Swing\_RL\_Local
* SFX\_Mace\_T1\_Swing\_Up\_Local
* SFX\_Mace\_T2\_Impact
* SFX\_Mace\_T2\_Raise
* SFX\_Mace\_T2\_Raise\_Local
* SFX\_Mace\_T2\_Signature\_Impact
* SFX\_Mace\_T2\_Signature\_Impact\_Local
* SFX\_Mace\_T2\_Signature\_Launch
* SFX\_Mace\_T2\_Signature\_Launch\_Local
* SFX\_Mace\_T2\_Swing
* SFX\_Mace\_T2\_Swing\_Charged
* SFX\_Mace\_T2\_Swing\_Charged\_LR\_Local
* SFX\_Mace\_T2\_Swing\_Charged\_RL\_Local
* SFX\_Mace\_T2\_Swing\_Charged\_Up\_Local
* SFX\_Mace\_T2\_Swing\_LR\_Local
* SFX\_Mace\_T2\_Swing\_RL\_Local
* SFX\_Mace\_T2\_Swing\_Up\_Local
* SFX\_Meerkat\_Alerted
* SFX\_Meerkat\_Death
* SFX\_Meerkat\_Hurt
* SFX\_Meerkat\_Idle
* SFX\_Memories\_Unlock\_Local
* SFX\_Metal\_Break
* SFX\_Metal\_Build
* SFX\_Metal\_Hit
* SFX\_Metal\_Land
* SFX\_Metal\_Walk
* SFX\_Moose\_Bull\_Alerted
* SFX\_Mouse\_Alerted
* SFX\_Mouse\_Death
* SFX\_Mouse\_Flee
* SFX\_Mouse\_Hurt
* SFX\_Mouse\_Run
* SFX\_Mouse\_Sleep
* SFX\_Mud\_Break
* SFX\_Mud\_Build
* SFX\_Mud\_Hit
* SFX\_Mud\_Land
* SFX\_Mud\_Walk
* SFX\_Mug\_Fill
* SFX\_Mug\_Fill\_Local
* SFX\_Mushroom\_Break
* SFX\_Mushroom\_Harvest
* SFX\_Music\_Ducking\_2db
* SFX\_NPC\_Unarmed\_Impact
* SFX\_NPC\_Unarmed\_Swing
* SFX\_Ore\_Break
* SFX\_Ore\_Hit
* SFX\_Outlander\_Hunter\_Arrow\_Hit
* SFX\_Outlander\_Hunter\_Arrow\_Miss
* SFX\_Outlander\_Hurt
* SFX\_Owl\_Alerted
* SFX\_Owl\_Death
* SFX\_Owl\_Hurt
* SFX\_Pickaxe\_T1\_Swing\_Down\_Local
* SFX\_Pickaxe\_T2\_Impact\_Nice
* SFX\_Pig\_Alerted
* SFX\_Pig\_Death
* SFX\_Pig\_Hurt
* SFX\_Pig\_Run
* SFX\_Pig\_Walk
* SFX\_Pigeon\_Death
* SFX\_Pigeon\_Hurt
* SFX\_Piglet\_Alerted
* SFX\_Piglet\_Death
* SFX\_Piglet\_Hurt
* SFX\_Piglet\_Run
* SFX\_Pistol\_Fire
* SFX\_Plant\_Break
* SFX\_Plant\_Hit
* SFX\_Plant\_MoveIn
* SFX\_Player\_Climb\_Down
* SFX\_Player\_Climb\_Side
* SFX\_Player\_Climb\_Up
* SFX\_Player\_Craft\_Item\_Inventory
* SFX\_Player\_Death
* SFX\_Player\_Death\_Drown
* SFX\_Player\_Death\_Fall
* SFX\_Player\_Drop\_Item
* SFX\_Player\_Fall
* SFX\_Player\_Glide\_Motion
* SFX\_Player\_Glide\_Stationary
* SFX\_Player\_Grab\_Item
* SFX\_Player\_Hurt
* SFX\_Player\_Hurt\_Burn
* SFX\_Player\_Hurt\_Drowning
* SFX\_Player\_Hurt\_Fall
* SFX\_Player\_Jump
* SFX\_Player\_Mantle
* SFX\_Player\_Pickup\_Item
* SFX\_Player\_Roll
* SFX\_Player\_Slide
* SFX\_Player\_Swim
* SFX\_Player\_Swim\_Fast
* SFX\_Player\_Swim\_Jump
* SFX\_Player\_Unarmed\_Swing\_Left
* SFX\_Player\_Unarmed\_Swing\_Right
* SFX\_Plushie\_Break
* SFX\_Plushie\_Build
* SFX\_Poop\_Bounce
* SFX\_Poop\_Break
* SFX\_Poop\_Hit
* SFX\_Poop\_Walk
* SFX\_Portal\_Neutral
* SFX\_Portal\_Neutral\_Open
* SFX\_Portal\_Neutral\_Teleport\_Local
* SFX\_Portal\_Void
* SFX\_Potion\_Drink\_Success
* SFX\_Processing\_Placeholder
* SFX\_Projectile\_Poop\_Hit
* SFX\_Rabbit\_Alerted
* SFX\_Rabbit\_Death
* SFX\_Rabbit\_Hurt
* SFX\_Rabbit\_Run
* SFX\_Rabbit\_Sleep
* SFX\_Ram\_Alerted
* SFX\_Ram\_Death
* SFX\_Ram\_Hurt
* SFX\_Ram\_Run
* SFX\_Ram\_Sleep
* SFX\_Raptor\_Cave\_Alerted
* SFX\_Raptor\_Cave\_Idle
* SFX\_Rat\_Death
* SFX\_Rat\_Hurt
* SFX\_Raven\_Alerted
* SFX\_Raven\_Death
* SFX\_Raven\_Flee
* SFX\_Raven\_Hurt
* SFX\_Reeds\_MoveIn
* SFX\_Rifle\_Fire
* SFX\_Rifle\_Fire\_Local
* SFX\_Rope\_Break
* SFX\_Rope\_Build
* SFX\_Rope\_Land
* SFX\_Rope\_Walk
* SFX\_Rotate\_Pitch\_Default
* SFX\_Rotate\_Roll\_Default
* SFX\_Rotate\_Yaw\_Default
* SFX\_Rubble\_Bounce
* SFX\_Rubble\_Hit
* SFX\_Sand\_Break
* SFX\_Sand\_Build
* SFX\_Sand\_Hit
* SFX\_Sand\_Land
* SFX\_Sand\_Walk
* SFX\_Scarak\_Fighter\_Alerted
* SFX\_Scarak\_Fighter\_Death
* SFX\_Scarak\_Fighter\_Hurt
* SFX\_Scarak\_Seeker\_Alerted
* SFX\_Scarak\_Seeker\_Death
* SFX\_Scarak\_Seeker\_Hurt
* SFX\_Scarak\_Seeker\_Spitball\_Death
* SFX\_Scarak\_Spitball\_Fire
* SFX\_Scorpion\_Alerted
* SFX\_Scorpion\_Death
* SFX\_Scorpion\_Run
* SFX\_Scorpion\_Threaten
* SFX\_Seeds\_Place
* SFX\_Shark\_Death
* SFX\_Shark\_Dive
* SFX\_Shark\_Hurt
* SFX\_Shark\_Swim
* SFX\_Shears\_Activate
* SFX\_Sheep\_Alerted
* SFX\_Sheep\_Death
* SFX\_Sheep\_Hurt
* SFX\_Sheep\_Run
* SFX\_Sheep\_Sheared
* SFX\_Sheep\_Walk
* SFX\_Shield\_T1\_Break
* SFX\_Shield\_T1\_Impact
* SFX\_Shield\_T1\_Raise
* SFX\_Shield\_T1\_Raise\_Local
* SFX\_Shield\_T1\_Swing
* SFX\_Shield\_T1\_Swing\_Local
* SFX\_Shield\_T2\_Impact
* SFX\_Shield\_T2\_Raise
* SFX\_Shield\_T2\_Raise\_Local
* SFX\_Shield\_T2\_Swing
* SFX\_Shield\_T2\_Swing\_Local
* SFX\_Shovel\_T1\_Swing\_RL\_Local
* SFX\_Shovel\_T2\_Impact\_Nice
* SFX\_Skeleton\_Alerted
* SFX\_Skeleton\_Death\_1
* SFX\_Skeleton\_Death\_2
* SFX\_Skeleton\_Death\_3
* SFX\_Skeleton\_Death\_4
* SFX\_Skeleton\_Despawn\_1
* SFX\_Skeleton\_Despawn\_2
* SFX\_Skeleton\_Hurt
* SFX\_Skeleton\_Mage\_Spellbook\_Charge
* SFX\_Skeleton\_Mage\_Spellbook\_Impact
* SFX\_Skeleton\_Praetorian\_Alerted
* SFX\_Skeleton\_Praetorian\_Death\_1
* SFX\_Skeleton\_Praetorian\_Death\_2
* SFX\_Skeleton\_Praetorian\_Death\_3
* SFX\_Skeleton\_Praetorian\_Death\_4
* SFX\_Skeleton\_Praetorian\_Despawn\_1
* SFX\_Skeleton\_Praetorian\_Despawn\_2
* SFX\_Skeleton\_Praetorian\_Hurt
* SFX\_Skeleton\_Praetorian\_Run
* SFX\_Skeleton\_Praetorian\_Search\_2
* SFX\_Skeleton\_Praetorian\_Spawn\_1
* SFX\_Skeleton\_Praetorian\_Walk
* SFX\_Skeleton\_Run
* SFX\_Skeleton\_Search\_2
* SFX\_Skeleton\_Spawn\_1
* SFX\_Skeleton\_Spawn\_2
* SFX\_Skeleton\_Walk
* SFX\_Snake\_Alerted
* SFX\_Snake\_Death
* SFX\_Snake\_Hurt
* SFX\_Snake\_Idle
* SFX\_Snow\_Break
* SFX\_Snow\_Build
* SFX\_Snow\_Hit
* SFX\_Snow\_Land
* SFX\_Snow\_Walk
* SFX\_Soft\_Break
* SFX\_Soft\_Build
* SFX\_Soft\_Hit
* SFX\_Soft\_Land
* SFX\_Soft\_Walk
* SFX\_Spark\_Living\_Alerted
* SFX\_Spark\_Living\_Death
* SFX\_Sparrow\_Alerted
* SFX\_Sparrow\_Death
* SFX\_Sparrow\_Hurt
* SFX\_Sparrow\_Idle
* SFX\_Spawn\_Void\_Alerted
* SFX\_Spawn\_Void\_Attack
* SFX\_Spawn\_Void\_Death
* SFX\_Spawn\_Void\_Hurt
* SFX\_Spawn\_Void\_Run
* SFX\_Spear\_Impact
* SFX\_Spear\_Lunge
* SFX\_Spear\_Lunge\_Local
* SFX\_Spear\_Miss
* SFX\_Spear\_Projectile\_Impact
* SFX\_Spear\_Throw
* SFX\_Spear\_Throw\_Charge
* SFX\_Spear\_Throw\_Charge\_Local
* SFX\_Spear\_Throw\_Local
* SFX\_Spider\_Alerted
* SFX\_Spider\_Death
* SFX\_Spider\_Run
* SFX\_Spirit\_Root\_Alerted
* SFX\_Spirit\_Root\_Death\_01
* SFX\_Spirit\_Root\_Death\_02
* SFX\_Spirit\_Root\_Hurt
* SFX\_Spirit\_Root\_Spawn
* SFX\_Squirrel\_Alerted
* SFX\_Squirrel\_Death
* SFX\_Squirrel\_Hurt
* SFX\_Squirrel\_Run
* SFX\_Staff\_Charged\_Loop
* SFX\_Staff\_Fire\_Shoot
* SFX\_Staff\_Flame\_Consume\_Charge\_1
* SFX\_Staff\_Flame\_Consume\_Charge\_1\_Local
* SFX\_Staff\_Flame\_Consume\_Charge\_2
* SFX\_Staff\_Flame\_Consume\_Charge\_2\_Local
* SFX\_Staff\_Flame\_Consume\_Charge\_3
* SFX\_Staff\_Flame\_Consume\_Charge\_3\_Local
* SFX\_Staff\_Flame\_Consume\_Charge\_4
* SFX\_Staff\_Flame\_Consume\_Charge\_4\_Local
* SFX\_Staff\_Flame\_Fireball\_Impact
* SFX\_Staff\_Flame\_Fireball\_Launch
* SFX\_Staff\_Flame\_Fireball\_Launch\_Local
* SFX\_Staff\_Flame\_Flamethrower
* SFX\_Staff\_Flame\_Flamethrower\_End
* SFX\_Staff\_Flame\_Flamethrower\_End\_Local
* SFX\_Staff\_Flame\_Flamethrower\_Impact
* SFX\_Staff\_Flame\_Flamethrower\_Local
* SFX\_Staff\_Flame\_Trap\_Deploy
* SFX\_Staff\_Flame\_Trap\_Despawn
* SFX\_Staff\_Flame\_Trap\_Loop
* SFX\_Staff\_Ice\_Shoot
* SFX\_Stamina\_Potion\_Success
* SFX\_Sticks\_Break
* SFX\_Stone\_Break
* SFX\_Stone\_Build
* SFX\_Stone\_Coffin\_Open\_Close
* SFX\_Stone\_Harvest
* SFX\_Stone\_Hit
* SFX\_Stone\_Land
* SFX\_Stone\_Walk
* SFX\_Sword\_T1\_Block\_Local
* SFX\_Sword\_T1\_Lunge\_Charge\_Local
* SFX\_Sword\_T1\_Lunge\_Local
* SFX\_Sword\_T1\_Shove\_Local
* SFX\_Sword\_T1\_Swing\_Down\_Local
* SFX\_Sword\_T1\_Swing\_LR\_Local
* SFX\_Sword\_T1\_Swing\_RL\_Local
* SFX\_Sword\_T2\_Block\_Local
* SFX\_Sword\_T2\_Impact
* SFX\_Sword\_T2\_Lunge\_Charge\_Local
* SFX\_Sword\_T2\_Lunge\_Local
* SFX\_Sword\_T2\_Signature\_Part\_1
* SFX\_Sword\_T2\_Signature\_Part\_1\_Local
* SFX\_Sword\_T2\_Signature\_Part\_2
* SFX\_Sword\_T2\_Signature\_Part\_2\_Local
* SFX\_Sword\_T2\_Swing\_Down\_Local
* SFX\_Sword\_T2\_Swing\_LR\_Local
* SFX\_Sword\_T2\_Swing\_RL\_Local
* SFX\_T1\_Impact\_Blunt
* SFX\_Tall\_Grass\_MoveIn
* SFX\_Test\_Blip\_A
* SFX\_Test\_Blip\_B
* SFX\_Test\_Blip\_C
* SFX\_Tetrabird\_Alerted
* SFX\_Tetrabird\_Death
* SFX\_Tetrabird\_Flee
* SFX\_Tetrabird\_Hurt
* SFX\_Tetrabird\_Run
* SFX\_Tiger\_Sabertooth\_Alerted
* SFX\_Tiger\_Sabertooth\_Death
* SFX\_Tiger\_Sabertooth\_Hurt
* SFX\_Tiger\_Sabertooth\_Run
* SFX\_Toad\_Rhino\_Alerted
* SFX\_Toad\_Rhino\_Death
* SFX\_Toad\_Rhino\_Hurt
* SFX\_Toad\_Rhino\_Magma\_Alerted
* SFX\_Toad\_Rhino\_Magma\_Death
* SFX\_Toad\_Rhino\_Magma\_Hurt
* SFX\_Toad\_Rhino\_Magma\_Run
* SFX\_Toad\_Rhino\_Magma\_Tongue\_Impact
* SFX\_Toad\_Rhino\_Magma\_Tongue\_Whoosh
* SFX\_Toad\_Rhino\_Run
* SFX\_Toad\_Rhino\_Tongue\_Impact
* SFX\_Toad\_Rhino\_Tongue\_Whoosh
* SFX\_Tombstone\_Break
* SFX\_Tool\_T1\_Swing
* SFX\_Tool\_Watering\_Can\_Water
* SFX\_Torch\_Break
* SFX\_Torch\_Build
* SFX\_Torch\_Default\_Loop
* SFX\_Torch\_Impact
* SFX\_Torch\_Off
* SFX\_Torch\_On\_Loop
* SFX\_Torch\_Swing
* SFX\_Torch\_Swing\_Left\_Local
* SFX\_Torch\_Swing\_Right\_Local
* SFX\_Tornado
* SFX\_Trashpile\_Land
* SFX\_Trashpile\_Walk
* SFX\_Trork\_Alerted
* SFX\_Trork\_Chieftain\_Alerted
* SFX\_Trork\_Chieftain\_Death
* SFX\_Trork\_Chieftain\_Hurt
* SFX\_Trork\_Chieftain\_Run
* SFX\_Trork\_Chieftain\_Search
* SFX\_Trork\_Death
* SFX\_Trork\_Exertion
* SFX\_Trork\_Hurt\_01
* SFX\_Trork\_Hurt\_02
* SFX\_Trork\_Run
* SFX\_Trork\_Search
* SFX\_Trork\_Sleep
* SFX\_Trork\_Throwing\_Axe
* SFX\_Unarmed\_Impact
* SFX\_Unarmed\_Swing
* SFX\_Unbreakable\_Block
* SFX\_Vulture\_Alerted
* SFX\_Vulture\_Death
* SFX\_Vulture\_Flee
* SFX\_Vulture\_Hurt
* SFX\_Wand\_Fire\_Shoot
* SFX\_Wand\_Ice\_Shoot
* SFX\_Warthog\_Alerted
* SFX\_Warthog\_Death
* SFX\_Warthog\_Hurt
* SFX\_Warthog\_Piglet\_Alerted
* SFX\_Warthog\_Piglet\_Death
* SFX\_Warthog\_Piglet\_Hurt
* SFX\_Warthog\_Piglet\_Run
* SFX\_Warthog\_Run
* SFX\_Warthog\_Sleep
* SFX\_Warthog\_Walk
* SFX\_Water\_MoveIn
* SFX\_Water\_MoveOut
* SFX\_Weapon\_Bench\_Close
* SFX\_Weapon\_Bench\_Craft
* SFX\_Weapon\_Bench\_Open
* SFX\_Weapon\_Charge\_Swing
* SFX\_Web\_MoveIn
* SFX\_Window\_Break
* SFX\_Window\_Stone\_Break
* SFX\_Wisp\_Lamp\_Loop
* SFX\_Wolf\_Alerted
* SFX\_Wolf\_Death
* SFX\_Wolf\_Hurt
* SFX\_Wolf\_Run
* SFX\_Wolf\_Sleep
* SFX\_Wood\_Break
* SFX\_Wood\_Build
* SFX\_Wood\_Hit
* SFX\_Wood\_Land
* SFX\_Wood\_Walk
* SFX\_Woodpecker\_Death
* SFX\_Woodpecker\_Hurt
* SFX\_Workbench\_Close
* SFX\_Workbench\_Craft
* SFX\_Workbench\_Open
* SFX\_Workbench\_Upgrade\_Complete\_Default
* SFX\_Workbench\_Upgrade\_Start\_Default
* SFX\_Yeti\_Alerted
* SFX\_Z1\_Emit\_Forest\_Autumn\_Day\_Birds
* SFX\_Z1\_Emit\_Forest\_Autumn\_Day\_Insects
* SFX\_Z1\_Emit\_Forest\_Autumn\_Day\_Wind
* SFX\_Z1\_Emit\_Forest\_Azure\_Day\_Insects
* SFX\_Z1\_Emit\_Forest\_Azure\_Day\_Wind
* SFX\_Z1\_Emit\_Forest\_Gen\_Day\_Birds
* SFX\_Z1\_Emit\_Forest\_Gen\_Day\_Insects
* SFX\_Z1\_Emit\_Forest\_Gen\_Day\_Winds
* SFX\_Z1\_Emit\_Forest\_Moss\_Day\_Birds
* SFX\_Z1\_Emit\_Forest\_Moss\_Day\_Insects
* SFX\_Z1\_Emit\_Forest\_Moss\_Day\_Wind
* SFX\_Z1\_Emit\_Forest\_Night\_Birds
* SFX\_Z1\_Emit\_Forest\_Night\_Insects
* SFX\_Z1\_Emit\_Forest\_Night\_Wind
* SFX\_Z1\_Emit\_Kweebec\_Village\_Wind
* SFX\_Z1\_Emit\_Mountain\_Day\_Birds
* SFX\_Z1\_Emit\_Plains\_Gen\_Day\_Birds
* SFX\_Z1\_Emit\_Plains\_Gen\_Day\_Insects
* SFX\_Z1\_Emit\_Plains\_Gen\_Day\_Wind
* SFX\_Z1\_Emit\_Plains\_Gen\_Night\_Birds
* SFX\_Z1\_Emit\_Plains\_Gen\_Night\_Insects
* SFX\_Z1\_Emit\_Plains\_Gen\_Night\_Wind
* SFX\_Z1\_Emit\_Shore\_Day\_Birds
* SFX\_Z1\_Emit\_Shore\_Waves
* SFX\_Z1\_Emit\_Shore\_Wind
* SFX\_Z1\_Emit\_Swamp\_Day\_Birds
* SFX\_Z1\_Emit\_Swamp\_Day\_Frogs
* SFX\_Z1\_Emit\_Swamp\_Day\_Insects
* SFX\_Z1\_Emit\_Swamp\_Day\_Wind
* SFX\_Z1\_Emit\_Swamp\_Night\_Frogs
* SFX\_Z1\_Emit\_Trork\_Camp
* SFX\_Z1\_Shore\_Day\_Birds
* SFX\_Z3\_Emit\_Cave\_Ice\_Crackle
* SFX\_Z3\_Emit\_Cave\_Ice\_Rumble
* SFX\_Z3\_Emit\_Cave\_Ice\_Stress
* SFX\_Z3\_Emit\_Cave\_Snow\_Crackle
* SFX\_Z3\_Emit\_Cave\_Snow\_Melt
* SFX\_Z3\_Emit\_Hedera\_FX
* SFX\_Z3\_Emit\_Hedera\_PlantRustle
* SFX\_Z3\_Emit\_Tree\_Creak
* SFX\_Z3\_Emit\_Wind\_Leaves
* SFX\_Z3\_Emit\_Wind\_Leaves\_Stereo
* SFX\_Z3\_Forest\_Day\_Birds
* SFX\_Z3\_Forest\_Day\_General
* SFX\_Z3\_Forest\_Night\_Birds
* SFX\_Zombie\_Alerted
* SFX\_Zombie\_Attack\_Bite
* SFX\_Zombie\_Attack\_Swing
* SFX\_Zombie\_Death
* SFX\_Zombie\_Despawn
* SFX\_Zombie\_Hurt
* SFX\_Zombie\_Pursuit
* SFX\_Zombie\_ScratchBack
* SFX\_Zombie\_Spawn
--- Entities
Source: https://hytalemodding.dev/en/docs/server/entities
This page contains a list of all Entities available to be used as a key in the AssetMap.
* Antelope
* Archaeopteryx
* Armadillo
* Arrow\_Crossbow\_Signature
* Arrow\_Crude
* Arrow\_Fire
* Arrow\_Frost
* Arrow\_Iron
* Arrow\_Ricochet
* Arrow\_Ricochet\_Signature
* Arrow\_Shortbow\_Signature
* Arrow\_Vamp
* Arrow\_Vamp\_Signature
* Axe\_Bone
* Axe\_Stone\_Trork
* Bat
* Bat\_Ice
* Bear\_Grizzly
* Bear\_Polar
* Bison
* Bison\_Calf
* Bluebird
* Bluegill
* Boar
* Boar\_Piglet
* Boat
* Bomb
* Bomb\_Fire\_Goblin
* Bomb\_Fire\_Goblin\_Dud
* Bomb\_Large\_Fire\_Goblin
* Bomb\_Popberry
* Bomb\_Potion\_Poison
* Boy\_Trail
* Bramblekin
* Bramblekin\_Shaman
* Bullet\_Blunderbuss
* Bunny
* Cactee
* Cactee\_Spike
* Calf
* Camel
* Camel\_Calf
* Cat
* Catfish
* Chick
* Chick\_Desert
* Chicken
* Chicken\_Desert
* Chicken\_Undead
* Clownfish
* Corgi
* Cow
* Cow\_Undead
* Crab
* Crawler\_Void
* Crocodile
* Crossbow\_Turret
* Crossbow\_Turret\_Item\_Projectile
* Crow
* Dagger\_Adamantite
* Dagger\_Bone
* Dagger\_Bronze
* Dagger\_Bronze\_Ancient
* Dagger\_Cobalt
* Dagger\_Copper
* Dagger\_Crude
* Dagger\_Doomed
* Dagger\_Fang\_Doomed
* Dagger\_Iron
* Dagger\_Mithril
* Dagger\_Onyxium
* Dagger\_Stone\_Trork
* Dagger\_Thorium
* Debug
* Deer\_Doe
* Deployable\_Fire\_Trap
* Deployable\_Fire\_Trap\_Preview
* Dog
* Dragon\_Fire
* Dragon\_Frost
* Dragon\_Void
* Duck
* Eel\_Moray
* Egg
* Emberwulf
* Eye\_Void
* Eye\_Void\_Blast
* Fen\_Stalker
* Feran
* Feran\_Burrower
* Feran\_Civilian
* Feran\_Cub
* Feran\_Longtooth
* Feran\_Sharptooth
* Feran\_Windwalker
* Feran\_Windwalker\_Wind\_Burst
* Feran\_Windwalker\_Wind\_Vortex
* Finch\_Green
* Fireball
* Flamingo
* Fox
* Frog\_Blue
* Frog\_Green
* Frog\_Orange
* Frostgill
* Gecko
* Ghoul
* Goat
* Goat\_Kid
* Goblin
* Goblin\_Boss
* Goblin\_Duke
* Goblin\_Duke\_Large
* Goblin\_Hermit
* Goblin\_Lobber
* Goblin\_Miner
* Goblin\_Ogre
* Goblin\_Scrapper
* Goblin\_Thief
* Golem\_Crystal\_Earth
* Golem\_Crystal\_Flame
* Golem\_Crystal\_Frost
* Golem\_Crystal\_Sand
* Golem\_Crystal\_Thunder
* Golem\_Firesteel
* Golem\_Guardian\_Void
* Grooble
* Hatworm
* Hawk
* Healing\_Totem
* Healing\_Totem\_Projectile
* Hedera
* Horse
* Horse\_Foal
* Horse\_Skeleton
* Horse\_Skeleton\_Armored
* Hound\_Bleached
* Hyena
* Ice\_Ball
* Ice\_Bolt
* Ingredient\_Poop
* Jellyfish\_Blue
* Jellyfish\_Cyan
* Jellyfish\_Green
* Jellyfish\_Man\_Of\_War
* Jellyfish\_Red
* Jellyfish\_Yellow
* Klops
* Klops\_Gentleman
* Klops\_Merchant
* Klops\_Miner
* Kunai
* Kweebec\_Rootling
* Kweebec\_Sapling
* Kweebec\_Sapling\_Brown
* Kweebec\_Sapling\_Christmas\_Blue
* Kweebec\_Sapling\_Christmas\_Green
* Kweebec\_Sapling\_Christmas\_Pink
* Kweebec\_Sapling\_Green
* Kweebec\_Sapling\_HardHat
* Kweebec\_Sapling\_Orange
* Kweebec\_Sapling\_Pink
* Kweebec\_Sapling\_Razorleaf
* Kweebec\_Sapling\_Red
* Kweebec\_Sapling\_Treesinger
* Kweebec\_Sapling\_Yellow
* Kweebec\_Seedling
* Kweebec\_Sproutling
* Kweebec\_Sproutling\_Blue
* Kweebec\_Sproutling\_Lime
* Lamb
* Larva\_Silk
* Larva\_Void
* Leopard\_Snow
* Lizard\_Sand
* Lobster
* Mannequin
* Meerkat
* Minecart
* Minnow
* Model\_Bee\_Swarm
* Model\_Deer\_Stag
* Molerat
* Moose\_Bull
* Moose\_Cow
* Mosshorn
* Mosshorn\_Plain
* Mouflon
* Mouflon\_Lamb
* Mouse
* Mushee
* NPC\_Elf
* NPC\_Path\_Marker
* NPC\_Santa
* NPC\_Sound\_Shoe
* NPC\_Spawn\_Marker
* Necromancer\_Void
* Objective\_Location\_Marker
* Outlander
* Outlander\_Berserker
* Outlander\_Brute
* Outlander\_Cultist
* Outlander\_Hunter
* Outlander\_Marauder
* Outlander\_Peon
* Outlander\_Priest
* Outlander\_Sorcerer
* Outlander\_Stalker
* Owl\_Brown
* Owl\_Snow
* Parrot
* Penguin
* Pig
* Pig\_Undead
* Pig\_Wild
* Pigeon
* Piglet
* Piglet\_Wild
* Pike
* Piranha
* Piranha\_Black
* Player
* PlayerTestModel\_G
* PlayerTestModel\_V
* Projectile
* Pterodactyl
* Pufferfish
* Rabbit
* Ram
* Ram\_Lamb
* Raptor\_Cave
* Rat
* Raven
* Reindeer\_Christmas
* Rex\_Cave
* Rubble\_Aqua
* Rubble\_Basalt
* Rubble\_Calcite
* Rubble\_Default
* Rubble\_Ice
* Rubble\_Marble
* Rubble\_Quartzite
* Rubble\_Sandstone
* Rubble\_Sandstone\_Red
* Rubble\_Sandstone\_White
* Rubble\_Shale
* Rubble\_Slate
* Rubble\_Stone
* Rubble\_Stone\_Mossy
* Rubble\_Volcanic
* Salmon
* Saurian
* Saurian\_Hunter
* Saurian\_Rogue
* Saurian\_Warrior
* Scarak\_Broodmother
* Scarak\_Broodmother\_Young
* Scarak\_Defender
* Scarak\_Fighter
* Scarak\_Fighter\_Royal\_Guard
* Scarak\_Louse
* Scarak\_Seeker
* Scarak\_Seeker\_Spitball
* Scorpion
* Shadow\_Knight
* Shark\_Hammerhead
* Sheep
* Shellfish\_Lava
* Showcase\_Cobalt\_Gear
* Showcase\_Copper\_Gear
* Showcase\_Iron\_Gear
* Showcase\_Iron\_TargetDummy\_1
* Showcase\_Mannequin\_Heal
* Showcase\_Mannequin\_Inv\_Portal
* Showcase\_Mannequin\_Inv\_Sphere
* Showcase\_Mannequin\_Lightning
* Showcase\_Mannequin\_Sitting
* Showcase\_Onyxium\_Gear
* Showcase\_Prisma\_Gear
* Showcase\_Skeleton\_Assasin
* Showcase\_Skeleton\_Dead
* Showcase\_Skeleton\_Guard
* Showcase\_Skeleton\_Tank
* Showcase\_Wooden\_Gear
* Skeleton
* Skeleton\_Archer
* Skeleton\_Archmage
* Skeleton\_Burnt\_Alchemist
* Skeleton\_Burnt\_Archer
* Skeleton\_Burnt\_Gunner
* Skeleton\_Burnt\_Knight
* Skeleton\_Burnt\_Lancer
* Skeleton\_Burnt\_Praetorian
* Skeleton\_Burnt\_Soldier
* Skeleton\_Burnt\_Wizard
* Skeleton\_Fighter
* Skeleton\_Frost\_Archer
* Skeleton\_Frost\_Archmage
* Skeleton\_Frost\_Fighter
* Skeleton\_Frost\_Knight
* Skeleton\_Frost\_Mage
* Skeleton\_Frost\_Ranger
* Skeleton\_Frost\_Scout
* Skeleton\_Frost\_Soldier
* Skeleton\_Incandescent\_Fighter
* Skeleton\_Incandescent\_Footman
* Skeleton\_Incandescent\_Head
* Skeleton\_Incandescent\_Mage
* Skeleton\_Knight
* Skeleton\_Mage
* Skeleton\_Mage\_Corruption\_Orb
* Skeleton\_Pirate\_Captain
* Skeleton\_Pirate\_Gunner
* Skeleton\_Pirate\_Striker
* Skeleton\_Ranger
* Skeleton\_Sand\_Archer
* Skeleton\_Sand\_Archmage
* Skeleton\_Sand\_Assassin
* Skeleton\_Sand\_Guard
* Skeleton\_Sand\_Mage
* Skeleton\_Sand\_Ranger
* Skeleton\_Sand\_Scout
* Skeleton\_Sand\_Soldier
* Skeleton\_Scout
* Skeleton\_Soldier
* Skrill
* Skrill\_Chick
* Slothian
* Slothian\_Elder
* Slothian\_Kid
* Slothian\_Monk
* Slothian\_Scout
* Slothian\_Villager
* Slothian\_Warrior
* Slowness\_Totem
* Slowness\_Totem\_Projectile
* Slug\_Magma
* Snail\_Frost
* Snail\_Magma
* Snake\_Cobra
* Snake\_Marsh
* Snake\_Rattle
* Snapdragon
* Snapjaw
* Spark\_Living
* Sparrow
* Spawn\_Void
* Spear\_Adamantite
* Spear\_Adamantite\_Saurian
* Spear\_Bone
* Spear\_Bronze
* Spear\_Cobalt
* Spear\_Copper
* Spear\_Crude
* Spear\_Double\_Incandescent
* Spear\_Iron
* Spear\_Leaf
* Spear\_Mithril
* Spear\_Onyxium
* Spear\_Scrap
* Spear\_Stone\_Trork
* Spear\_Thorium
* Spear\_Tribal
* Spectre\_Void
* Spider
* Spider\_Cave
* Spirit\_Ember
* Spirit\_Frost
* Spirit\_Root
* Spirit\_Thunder
* Squirrel
* Swarm\_Bees
* Sword\_Charged\_Test
* Tang\_Blue
* Tang\_Chevron
* Tang\_Lemon\_Peel
* Tang\_Sailfin
* Tank
* Temple\_Mithril\_Guard
* Test\_Platform
* Tetrabird
* Tiger\_Sabertooth
* Toad\_Rhino
* Toad\_Rhino\_Magma
* Tornado
* Tortoise
* Trash
* Trillodon
* Trilobite
* Trilobite\_Black
* Trork
* Trork\_Brawler
* Trork\_Chieftain
* Trork\_Christmas
* Trork\_Doctor\_Witch
* Trork\_Guard
* Trork\_Hunter
* Trork\_Mauler
* Trork\_Sentry
* Trork\_Shaman
* Trork\_Warrior
* Trout\_Rainbow
* Tuluk
* Tuluk\_Fisherman
* Turkey
* Turkey\_Chick
* Vulture
* Warp
* Warrior\_Quest
* Warthog
* Warthog\_Piglet
* Werewolf
* Whale\_Humpback
* Wolf\_Black
* Wolf\_Outlander\_Priest
* Wolf\_Outlander\_Sorcerer
* Wolf\_Trork\_Hunter
* Wolf\_Trork\_Shaman
* Wolf\_White
* Woodpecker
* Wraith
* Wraith\_Lantern
* Wurmling\_Frost
* Yeti
* Zombie
* Zombie\_Aberrant
* Zombie\_Aberrant\_Big
* Zombie\_Aberrant\_Small
* Zombie\_Burnt
* Zombie\_Frost
* Zombie\_Sand
* Zombie\_Werewolf
--- NPC Meta
Source: https://hytalemodding.dev/en/docs/official-documentation/npc-doc
# List of Builders
| | Name | Type | Description |
| --- | ------------------------------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | [Abstract](#role_abstract) | Role | Generic role for NPC |
| 2 | [ActionList](#actionlist_actionlist) | ActionList | An array of actions to be executed |
| 3 | [AddToHostileTargetMemory](#action_addtohostiletargetmemory) | Action | Adds the passed target from the sensor to the hostile target memory |
| 4 | [AdjustPosition](#sensor_adjustposition) | Sensor | Perform adjustments to the wrapped sensor's returned position |
| 5 | [Age](#sensor_age) | Sensor | Triggers when the age of the NPC falls between a certain range |
| 6 | [Aim](#headmotion_aim) | HeadMotion | Aim at target |
| 7 | [AimCharge](#bodymotion_aimcharge) | BodyMotion | Aim the NPC at a target position for performing a charge based on aiming information and ensure that the charge is possible before it's executed. |
| 8 | [Alarm](#sensor_alarm) | Sensor | Check the state of a named alarm |
| 9 | [Altitude](#ientityfilter_altitude) | IEntityFilter | Matches targets if they're within the defined range above the ground |
| 10 | [And](#sensor_and) | Sensor | Logical AND of list of sensors |
| 11 | [And](#ientityfilter_and) | IEntityFilter | Logical AND of a list of filters |
| 12 | [Animation](#sensor_animation) | Sensor | Check if a given animation is being played |
| 13 | [Any](#sensor_any) | Sensor | Return always true |
| 14 | [Appearance](#action_appearance) | Action | Set model displayed for NPC |
| 15 | [ApplyEntityEffect](#action_applyentityeffect) | Action | Applies an entity effect to the target or self |
| 16 | [Attack](#action_attack) | Action | Starts attack |
| 17 | [Attitude](#isensorentityprioritiser_attitude) | ISensorEntityPrioritiser | Prioritises return entities by attitude |
| 18 | [Attitude](#ientityfilter_attitude) | IEntityFilter | Matches the attitude towards the locked target |
| 19 | [Beacon](#action_beacon) | Action | Send Beacon Message |
| 20 | [Beacon](#sensor_beacon) | Sensor | Checks to see if any messages have been broadcasted by nearby NPCs |
| 21 | [Block](#sensor_block) | Sensor | Checks for one of a set of blocks in the nearby area |
| 22 | [BlockChange](#sensor_blockchange) | Sensor | Matches when a block from a blockset within a certain range is changed or interacted with |
| 23 | [BlockType](#sensor_blocktype) | Sensor | Checks if the block at the given position matches the provided block set |
| 24 | [CanInteract](#sensor_caninteract) | Sensor | Checks whether or not the player being iterated by the interaction instruction can interact with this NPC |
| 25 | [CanPlaceBlock](#sensor_canplaceblock) | Sensor | Test if the currently set block can be placed at the relative position given direction and offset |
| 26 | [Combat](#ientityfilter_combat) | IEntityFilter | Check the target's combat state |
| 27 | [CombatAbility](#action_combatability) | Action | Starts the combat ability selected by the combat action evaluator. |
| 28 | [CombatActionEvaluator](#sensor_combatactionevaluator) | Sensor | A sensor which handles funnelling information to actions and motions from the combat action evaluator. |
| 29 | [CombatTargets](#isensorentitycollector_combattargets) | ISensorEntityCollector | A collector which processes matched friendly and hostile targets and adds them to the NPC's short-term combat memory. |
| 30 | [CompleteTask](#action_completetask) | Action | Complete a task |
| 31 | [Count](#sensor_count) | Sensor | Check if there is a certain number of NPCs or players within a specific range |
| 32 | [Crouch](#action_crouch) | Action | Set NPC crouching state |
| 33 | [Damage](#sensor_damage) | Sensor | Test if NPC suffered damage |
| 34 | [DelayDespawn](#action_delaydespawn) | Action | Delay the despawning cycle for some amount of time |
| 35 | [Despawn](#action_despawn) | Action | Trigger the NPC to despawn |
| 36 | [Die](#action_die) | Action | Kill the NPC |
| 37 | [DisplayName](#action_displayname) | Action | Set display name. |
| 38 | [Dive](#motioncontroller_dive) | MotionController | Provide diving abilities for NPC |
| 39 | [DropItem](#action_dropitem) | Action | Drop an item |
| 40 | [DroppedItem](#sensor_droppeditem) | Sensor | Triggers if a given item is within a certain range of the NPC. |
| 41 | [EntityEvent](#sensor_entityevent) | Sensor | Matches when an entity from a specific NPC group within a certain range is damaged, killed, or interacted with |
| 42 | [Eval](#sensor_eval) | Sensor | Evaluate javascript expression and test if true |
| 43 | [Flag](#sensor_flag) | Sensor | Test if a named flag is set or not |
| 44 | [Flee](#bodymotion_flee) | BodyMotion | Move away from target |
| 45 | [Flock](#bodymotion_flock) | BodyMotion | Flocking - WIP |
| 46 | [Flock](#ientityfilter_flock) | IEntityFilter | Test for flock membership and related properties |
| 47 | [FlockBeacon](#action_flockbeacon) | Action | Send beacon message to flock |
| 48 | [FlockCombatDamage](#sensor_flockcombatdamage) | Sensor | Test if flock with NPC received combat damage |
| 49 | [FlockLeader](#sensor_flockleader) | Sensor | Test for the presence and provide position of the flock leader |
| 50 | [FlockState](#action_flockstate) | Action | Set state name for flock. |
| 51 | [FlockTarget](#action_flocktarget) | Action | Set or clear locked target for flock. |
| 52 | [Fly](#motioncontroller_fly) | MotionController | Flight motion controller |
| 53 | [Generic](#role_generic) | Role | Generic role for NPC |
| 54 | [HasHostileTargetMemory](#sensor_hashostiletargetmemory) | Sensor | Checks if there is currently a hostile target in the target memory. |
| 55 | [HasInteracted](#sensor_hasinteracted) | Sensor | Checks whether the currently iterated player in the interaction instruction has interacted with this NPC |
| 56 | [HasTask](#sensor_hastask) | Sensor | Checks whether or not the player being iterated by the interaction instruction has any of the given tasks |
| 57 | [HashMap](#hashmap_hashmap) | HashMap | List of motion controllers |
| 58 | [HeightDifference](#ientityfilter_heightdifference) | IEntityFilter | Matches entities within the given height range |
| 59 | [IgnoreForAvoidance](#action_ignoreforavoidance) | Action | Set the target slot of an entity that should be ignored during avoidance |
| 60 | [InAir](#sensor_inair) | Sensor | Test if NPC is not on ground |
| 61 | [InWater](#sensor_inwater) | Sensor | Check if NPC is currently in water |
| 62 | [InflictedDamage](#sensor_inflicteddamage) | Sensor | Test if an individual or the flock it belongs to inflicted combat damage |
| 63 | [InsideBlock](#ientityfilter_insideblock) | IEntityFilter | Matches if the entity is inside any of the blocks in the BlockSet |
| 64 | [Instruction](#instruction_instruction) | Instruction | An instruction with Sensor, and Motions and Actions, or a list of nested instructions. |
| 65 | [InteractionContext](#sensor_interactioncontext) | Sensor | Checks whether the currently iterated player in the interaction instruction has interacted with this NPC in the given context |
| 66 | [Inventory](#action_inventory) | Action | Add or remove items from inventory. |
| 67 | [Inventory](#ientityfilter_inventory) | IEntityFilter | Test various conditions relating to entity inventory |
| 68 | [IsBackingAway](#sensor_isbackingaway) | Sensor | Test if the NPC is currently backing away from something. |
| 69 | [IsBusy](#sensor_isbusy) | Sensor | Tests if an NPC is in one of the defined Busy States. |
| 70 | [ItemInHand](#ientityfilter_iteminhand) | IEntityFilter | Check if entity is holding an item |
| 71 | [JoinFlock](#action_joinflock) | Action | Join/build a flock with other entity |
| 72 | [Kill](#sensor_kill) | Sensor | Test if NPC made a kill |
| 73 | [Land](#bodymotion_land) | BodyMotion | Try to land at the given position |
| 74 | [Leash](#sensor_leash) | Sensor | Triggers when the NPC is outside a specified range from the leash point |
| 75 | [Leave](#bodymotion_leave) | BodyMotion | Leave place |
| 76 | [LeaveFlock](#action_leaveflock) | Action | Leave flock. |
| 77 | [Light](#sensor_light) | Sensor | Check the light levels of the block an entity is standing on |
| 78 | [LineOfSight](#ientityfilter_lineofsight) | IEntityFilter | Matches if there is line of sight to the target |
| 79 | [LockOnInteractionTarget](#action_lockoninteractiontarget) | Action | Locks on to the currently iterated player in the interaction instruction |
| 80 | [Log](#action_log) | Action | Log a message to console. |
| 81 | [MaintainDistance](#bodymotion_maintaindistance) | BodyMotion | Maintain distance from a given position |
| 82 | [MakePath](#action_makepath) | Action | Constructs a transient path for the NPC based on a series of rotations and distances |
| 83 | [MatchLook](#bodymotion_matchlook) | BodyMotion | Make NPC body rotate to match look direction |
| 84 | [Mob](#sensor_mob) | Sensor | Test if entity matching specific attributes and filters is in range |
| 85 | [ModelAttachment](#action_modelattachment) | Action | Set an attachment on the current NPC model |
| 86 | [MotionController](#sensor_motioncontroller) | Sensor | Test if specific motion controller is active. |
| 87 | [Mount](#action_mount) | Action | Enable the player to mount the entity |
| 88 | [MovementState](#ientityfilter_movementstate) | IEntityFilter | Check if the entity is in the given movement state |
| 89 | [NPCGroup](#ientityfilter_npcgroup) | IEntityFilter | Returns whether the entity matches one of the provided NPCGroups |
| 90 | [Nav](#sensor_nav) | Sensor | Queries navigation state |
| 91 | [Not](#sensor_not) | Sensor | Invert sensor test |
| 92 | [Not](#ientityfilter_not) | IEntityFilter | Invert filter test |
| 93 | [Nothing](#action_nothing) | Action | Do nothing |
| 94 | [Nothing](#bodymotion_nothing) | BodyMotion | Do nothing |
| 95 | [Nothing](#headmotion_nothing) | HeadMotion | Do nothing |
| 96 | [Notify](#action_notify) | Action | Directly notifies a target NPC with a beacon message |
| 97 | [Observe](#headmotion_observe) | HeadMotion | Observe surroundings in various ways. |
| 98 | [OnGround](#sensor_onground) | Sensor | Test if NPC is on ground |
| 99 | [OpenBarterShop](#action_openbartershop) | Action | Open the barter shop UI for the current player |
| 100 | [OpenShop](#action_openshop) | Action | Open the shop UI for the current player |
| 101 | [Or](#sensor_or) | Sensor | Logical OR of list of sensors |
| 102 | [Or](#ientityfilter_or) | IEntityFilter | Logical OR of a list of filters |
| 103 | [OverrideAltitude](#action_overridealtitude) | Action | Temporarily override the preferred altitude of a flying NPC |
| 104 | [OverrideAttitude](#action_overrideattitude) | Action | Override this NPCs attitude towards the provided target for a given duration |
| 105 | [ParentState](#action_parentstate) | Action | Set the main state of NPC from within a component |
| 106 | [Path](#path_path) | Path | List of transient path points |
| 107 | [Path](#sensor_path) | Sensor | Find a path based on various criteria |
| 108 | [Path](#bodymotion_path) | BodyMotion | Walk along a path |
| 109 | [PickUpItem](#action_pickupitem) | Action | Pick up an item |
| 110 | [PlaceBlock](#action_placeblock) | Action | Place a block (chosen by another action) at a position returned by a Sensor if close enough |
| 111 | [PlayAnimation](#action_playanimation) | Action | Play an animation |
| 112 | [PlaySound](#action_playsound) | Action | Plays a sound to players within a specified range. |
| 113 | [Player](#sensor_player) | Sensor | Test if player matching specific attributes and filters is in range |
| 114 | [Random](#action_random) | Action | Execute a single random action from a list of weighted actions. |
| 115 | [Random](#sensor_random) | Sensor | Alternates between returning true and false for specified random durations |
| 116 | [Random](#instruction_random) | Instruction | Randomised list of weighted instructions. |
| 117 | [ReadPosition](#sensor_readposition) | Sensor | Read a stored position with some conditions |
| 118 | [RecomputePath](#action_recomputepath) | Action | Force recomputation of path finder solution |
| 119 | [Reference](#instruction_reference) | Instruction | Prioritized instruction list that can be referenced from elsewhere in the file |
| 120 | [RelativeWaypointDefinition](#relativewaypointdefinition_relativewaypointdefinition) | RelativeWaypointDefinition | A simple path waypoint definition where each waypoint is relative to the previous |
| 121 | [ReleaseTarget](#action_releasetarget) | Action | Clear locked target |
| 122 | [Remove](#action_remove) | Action | Erase the target entity from the world (no death animation). |
| 123 | [ResetBlockSensors](#action_resetblocksensors) | Action | Resets a specific block sensor by name, or all block sensors |
| 124 | [ResetInstructions](#action_resetinstructions) | Action | Force reset instructionList |
| 125 | [ResetPath](#action_resetpath) | Action | Resets the current patrol path this NPC follows. |
| 126 | [ResetSearchRays](#action_resetsearchrays) | Action | Resets a specific search ray sensor cached position by name, or all search ray sensors |
| 127 | [Role](#action_role) | Action | Change the Role of the NPC |
| 128 | [SearchRay](#sensor_searchray) | Sensor | Fire a ray at a specific angle to see if what it hits matches a given sought block |
| 129 | [Seek](#bodymotion_seek) | BodyMotion | Chase target |
| 130 | [Self](#sensor_self) | Sensor | Test if the NPC itself matches a set of entity filters |
| 131 | [Sequence](#action_sequence) | Action | List of actions. |
| 132 | [Sequence](#bodymotion_sequence) | BodyMotion | (Looped)Sequence of motions |
| 133 | [Sequence](#headmotion_sequence) | HeadMotion | (Looped)Sequence of motions |
| 134 | [SetAlarm](#action_setalarm) | Action | Set a named alarm on the NPC |
| 135 | [SetBlockToPlace](#action_setblocktoplace) | Action | Set the block type the NPC will place |
| 136 | [SetFlag](#action_setflag) | Action | Set a named flag to a boolean value |
| 137 | [SetInteractable](#action_setinteractable) | Action | Set whether the currently iterated player in the interaction instruction should be able to interact with this NPC |
| 138 | [SetLeashPosition](#action_setleashposition) | Action | Sets the NPCs current position to the spawn/leash position |
| 139 | [SetMarkedTarget](#action_setmarkedtarget) | Action | Explicitly sets a marked target in a given slot. |
| 140 | [SetStat](#action_setstat) | Action | Sets (or adds to) an entity stat on the NPC. |
| 141 | [Spawn](#action_spawn) | Action | Spawn an NPC |
| 142 | [SpawnParticles](#action_spawnparticles) | Action | Spawn particle system visible within a given range with an offset relative to npc heading |
| 143 | [SpotsMe](#ientityfilter_spotsme) | IEntityFilter | Checks if the entity can view the NPC in a given view sector or cone and without obstruction. |
| 144 | [StandingOnBlock](#ientityfilter_standingonblock) | IEntityFilter | Matches the block directly beneath the entity against a BlockSet |
| 145 | [StartObjective](#action_startobjective) | Action | Start the given objective for the currently iterated player in the interaction instruction |
| 146 | [Stat](#ientityfilter_stat) | IEntityFilter | Match stat values of the entity |
| 147 | [State](#action_state) | Action | Set state of NPC |
| 148 | [State](#sensor_state) | Sensor | Test for a specific state |
| 149 | [StateTransition](#statetransition_statetransition) | StateTransition | An entry containing a list of actions to execute when moving from one state to another |
| 150 | [StateTransitionController](#statetransitioncontroller_statetransitioncontroller) | StateTransitionController | A list of state transitions |
| 151 | [StateTransitionEdges](#statetransitionedges_statetransitionedges) | StateTransitionEdges | Sets of from and to states defining state transitions |
| 152 | [StorePosition](#action_storeposition) | Action | Store the position from the attached sensor |
| 153 | [Switch](#sensor_switch) | Sensor | Check if a computed boolean is true |
| 154 | [TakeOff](#bodymotion_takeoff) | BodyMotion | Switch NPC from walking to flying motion controller |
| 155 | [Target](#sensor_target) | Sensor | Test if given target matches a series of criteria and optional entity filters |
| 156 | [Teleport](#bodymotion_teleport) | BodyMotion | Teleport NPC to a position given by a sensor |
| 157 | [Test](#action_test) | Action | Test action to exercise attribute evaluation (DO NOT USE) |
| 158 | [TestProbe](#bodymotion_testprobe) | BodyMotion | Debugging - Test probing |
| 159 | [Time](#sensor_time) | Sensor | Check if the day/year time is within some specified time. |
| 160 | [Timeout](#action_timeout) | Action | Delay an action, or insert a delay in a sequence of actions |
| 161 | [Timer](#sensor_timer) | Sensor | Tests if a timer exists and the value is within a certain range |
| 162 | [Timer](#bodymotion_timer) | BodyMotion | Execute a Motion for a specific maximum time |
| 163 | [Timer](#headmotion_timer) | HeadMotion | Execute a Motion for a specific maximum time |
| 164 | [TimerContinue](#action_timercontinue) | Action | Continue a timer |
| 165 | [TimerModify](#action_timermodify) | Action | Modify values of a timer |
| 166 | [TimerPause](#action_timerpause) | Action | Pause a timer |
| 167 | [TimerRestart](#action_timerrestart) | Action | Restart a timer |
| 168 | [TimerStart](#action_timerstart) | Action | Start a timer |
| 169 | [TimerStop](#action_timerstop) | Action | Stop a timer |
| 170 | [ToggleStateEvaluator](#action_togglestateevaluator) | Action | Enable or disable the NPC's state evaluator |
| 171 | [TriggerSpawnBeacon](#action_triggerspawnbeacon) | Action | Trigger the nearest spawn beacon matching the configuration id |
| 172 | [TriggerSpawners](#action_triggerspawners) | Action | Trigger all, or up to a certain number of manual spawn markers in a radius around the NPC |
| 173 | [ValueProviderWrapper](#sensor_valueproviderwrapper) | Sensor | Wraps a sensor and passes down some additional parameter overrides pulled from the value store |
| 174 | [ValueToParameterMapping](#valuetoparametermapping_valuetoparametermapping) | ValueToParameterMapping | An entry containing a list of actions to execute when moving from one state to another |
| 175 | [Variant](#role_variant) | Role | Create a variant from an existing NPC JSON file |
| 176 | [ViewSector](#ientityfilter_viewsector) | IEntityFilter | Matches entities within the given view sector |
| 177 | [Walk](#motioncontroller_walk) | MotionController | Provide walk on ground abilities for NPC |
| 178 | [Wander](#bodymotion_wander) | BodyMotion | Random movement |
| 179 | [WanderInCircle](#bodymotion_wanderincircle) | BodyMotion | Random movement in circle around spawn position |
| 180 | [WanderInRect](#bodymotion_wanderinrect) | BodyMotion | Random movement in rectangle around spawn position |
| 181 | [Watch](#headmotion_watch) | HeadMotion | Rotate to target |
| 182 | [Weather](#sensor_weather) | Sensor | Matches the current weather at the NPCs position against a set of weather globs |
| 183 | [WeightedAction](#weightedaction_weightedaction) | WeightedAction | A wrapped and weighted action intended to be used for Random action lists. |
# List of Roles
## Abstract: Role (Stable)
Generic role for NPC with a core planner and list of Motion controllers.
### Attributes
#### - MaxHealth (Stable)
* Max health
* **Type**: `Integer`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (WorkInProgress)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - Appearance (Stable)
* Model to use for rendering
* **Type**: `Asset`, **Required**, **Computable**
#### - DisplayNames (Stable)
* List of possible display names to choose from
* **Type**: `StringList`, **Optional** (Default: null)
* **Constraint**: Strings in array must not be empty
#### - NameTranslationKey (Stable)
* The translation key for this NPC's name
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - OpaqueBlockSet (Stable)
* Blocks blocking line of sight
* **Type**: `Asset`, **Optional** (Default: Opaque)
#### - Inertia (Experimental)
* Inertia
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0.1
#### - KnockbackScale (Stable)
* Scale factor for knockback. Values greater 1 increase knockback. Smaller values decrease it.
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - InventorySize (Stable)
* Number of available inventory slots
* **Type**: `Integer`, **Optional** (Default: 0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 36
#### - HotbarSize (Stable)
* Number of available hotbar slots
* **Type**: `Integer`, **Optional** (Default: 3)
* **Constraint**: Value must be greater or equal than 3 and less or equal than 8
#### - OffHandSlots (Stable)
* The number of slots for off-hand items
* **Type**: `Integer`, **Optional** (Default: 0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 4
#### - HotbarItems (Stable)
* Hotbar items (e.g. primary weapon, secondary weapon, etc)
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
#### - OffHandItems (Stable)
* Off-hand items (e.g. shields, torches, etc)
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
#### - PossibleInventoryItems (Stable)
* A droplist defining the possible items the NPCs inventory could contain
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - DefaultOffHandSlot (Stable)
* The default off-hand item slot (-1 is empty)
* **Type**: `Integer`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 4
#### - Armor (WorkInProgress)
* Armor items
* **Type**: `AssetArray`, **Optional** (Default: null), **Element Type**: Item
#### - DropList (Stable)
* Drop list to spawn when killed
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - StartState (Stable)
* Initial state
* **Type**: `String`, **Optional** (Default: start)
* **Constraint**: String must be not empty
#### - DefaultSubState (Stable)
* The default sub state to reference when transitioning to a main state without a specified sub state
* **Type**: `String`, **Optional** (Default: Default)
* **Constraint**: String must be not empty
#### - CollisionDistance (Stable)
* Collision lookahead
* **Type**: `Double`, **Optional** (Default: 5.0)
* **Constraint**: Value must be greater than 0
#### - CollisionForceFalloff (Experimental)
* Falloff rate for collision force
* **Type**: `Double`, **Optional** (Default: 2.0)
* **Constraint**: Value must be greater than 0
#### - CollisionRadius (Experimental)
* Collision radius override
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - CollisionViewAngle (Experimental)
* Collision detection view cone
* **Type**: `Double`, **Optional** (Default: 320.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - SeparationDistance (Experimental)
* Desired separation distance
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SeparationWeight (Experimental)
* Blend factor separation
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - SeparationDistanceTarget (Experimental)
* Desired separation distance when close to target
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - SeparationNearRadiusTarget (Experimental)
* Distance when using SeparationDistanceTarget
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SeparationFarRadiusTarget (Experimental)
* Use normal separation distance from further than this distance
* **Type**: `Double`, **Optional** (Default: 5.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - ApplyAvoidance (Experimental)
* Apply avoidance steering force
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ApplySeparation (Experimental)
* Apply separation steering force
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - AvoidanceMode (Experimental)
* Abilities to use for avoidance
* **Type**: `Flag`, **Optional** (Default: Any)
* **Flag Values**:
* `Evade`: Only evade
* `Slowdown`: Only slow down NPC
* `Any`: Any avoidance allowed
#### - EntityAvoidanceStrength (Experimental)
* Blending factor avoidance
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - StayInEnvironment (Experimental)
* Stay in spawning environment
* **Type**: `Boolean`, **Optional** (Default: false)
#### - AllowedEnvironments (Experimental)
* Allowed environment to walk in
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - FlockSpawnTypes (WorkInProgress)
* Types of NPC this flock should consist off
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
#### - FlockSpawnTypesRandom (WorkInProgress)
* Create a randomized flock if true else spawn in order of FlockSpawnTypes
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - FlockAllowedNPC (Experimental)
* List of NPCs allowed in flock
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
#### - FlockCanLead (Experimental)
* This NPC can be flock leader
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - FlockWeightAlignment (Experimental)
* Blending flock alignment
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockWeightSeparation (Experimental)
* Blending flock separation
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockWeightCohesion (Experimental)
* Blending flock cohesion
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockInfluenceRange (Experimental)
* Influence radius flock forces
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - DisableDamageFlock (WorkInProgress)
* If true disables combat damage from flock members
* **Type**: `Boolean`, **Optional** (Default: true)
#### - DisableDamageGroups (WorkInProgress)
* Members in this list of group won't cause damage
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: TagSet
#### - BusyStates (Stable)
* States during which this NPC is busy and can't be interacted with
* **Type**: `StringList`, **Required**
* **Constraint**: String must be a valid state string. A main state must be included before the period (e.g. Main.Test). State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
#### - CombatConfig (Stable)
* The combat configuration providing optional combat action evaluator
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - Invulnerable (Stable)
* Makes NPC ignore damage
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - BreathesInAir (WorkInProgress)
* Can breath in air
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BreathesInWater (WorkInProgress)
* Can breath in fluid/water
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - PickupDropOnDeath (Stable)
* Drop last picked item on death
* **Type**: `Boolean`, **Optional** (Default: false)
#### - DeathAnimationTime (Experimental)
* How long to let the death animation play before removing
* **Type**: `Double`, **Optional** (Default: 5.0)
* **Constraint**: Value must be greater or equal than 0
#### - DeathInteraction (Experimental)
* Interaction to run on death
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - DespawnAnimationTime (Experimental)
* How long to let the despawn animation play before removing
* **Type**: `Double`, **Optional** (Default: 0.800000011920929)
* **Constraint**: Value must be greater or equal than 0
#### - SpawnParticles (Experimental)
* Particle system when spawning
* **Type**: `String`, **Optional** (Default: null)
#### - SpawnParticlesOffset (Experimental)
* Displacement from foot point to spawn relative to NPC heading
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Double
#### - SpawnViewDistance (Experimental)
* View distance for spawn particle
* **Type**: `Double`, **Optional** (Default: 75.0)
* **Constraint**: Value must be greater than 0
#### - DefaultPlayerAttitude (Stable)
* The default attitude of this NPC towards players
* **Type**: `Flag`, **Optional** (Default: HOSTILE), **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
#### - DefaultNPCAttitude (Stable)
* The default attitude of this NPC towards other NPCs
* **Type**: `Flag`, **Optional** (Default: NEUTRAL), **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
#### - AttitudeGroup (Stable)
* The attitude group towards other NPCs this NPC belongs to (often species related)
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - ItemAttitudeGroup (Stable)
* This NPC's item attitudes
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - CorpseStaysInFlock (Stable)
* Whether the NPC should stay in the flock until corpse removal or be removed at the moment of death
* **Type**: `Boolean`, **Optional** (Default: false)
#### - OverrideHeadPitchAngle (Experimental)
* Whether to override the head pitch angle range
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - HeadPitchAngleRange (Experimental)
* Head rotation pitch range to be used instead of model camera settings
* **Type**: `Array`, **Optional** (Default: \[-89.0, 89.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -90, less or equal than 90and in weakly ascending order
#### - MotionControllerList (Stable)
* Motion controllers
* **Type**: `ObjectRef`, **Required**, **Object Type**: HashMap
#### - Instructions (WorkInProgress)
* List of instructions
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Instruction
#### - InteractionInstruction (Stable)
* An instruction designed to evaluate and set which players can interact with an NPC, along with setting correct states upon interaction
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Instruction
#### - DeathInstruction (Stable)
* An instruction which will run only when the NPC is dead until they are removed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Instruction
#### - StateTransitions (Stable)
* A set of state transitions and the actions that will be executed during them
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: StateTransitionController
#### - StateEvaluator (Stable)
* A state evaluator
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - InitialMotionController (Stable)
* The initial motion controller to set. If omitted and there are multiple, one will be chosen at random.
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - InteractionVars (Stable)
* Interaction vars to be used in interactions.
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - IsMemory (Stable)
* Used to define if the NPC has a Memory to record.
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - MemoriesCategory (Stable)
* Category to put the NPC in, as part of the Memories Plugin
* **Type**: `String`, **Optional** (Default: Other), **Computable**
* **Constraint**: String value must be either null or not empty
#### - MemoriesNameOverride (Stable)
* Overrides the Memory name when set.
* **Type**: `String`, **Optional** (Default: ), **Computable**
#### - SpawnLockTime (Stable)
* How long the NPC should be locked and unable to perform behavior when first spawned
* **Type**: `Double`, **Optional** (Default: 1.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
### Constraints
* At least one of BreathesInAir, BreathesInWater must be true
[(Top)](#top)
## Generic: Role (Stable)
Generic role for NPC with a core planner and list of Motion controllers.
### Attributes
#### - MaxHealth (Stable)
* Max health
* **Type**: `Integer`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (WorkInProgress)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - Appearance (Stable)
* Model to use for rendering
* **Type**: `Asset`, **Required**, **Computable**
#### - DisplayNames (Stable)
* List of possible display names to choose from
* **Type**: `StringList`, **Optional** (Default: null)
* **Constraint**: Strings in array must not be empty
#### - NameTranslationKey (Stable)
* The translation key for this NPC's name
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - OpaqueBlockSet (Stable)
* Blocks blocking line of sight
* **Type**: `Asset`, **Optional** (Default: Opaque)
#### - Inertia (Experimental)
* Inertia
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0.1
#### - KnockbackScale (Stable)
* Scale factor for knockback. Values greater 1 increase knockback. Smaller values decrease it.
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - InventorySize (Stable)
* Number of available inventory slots
* **Type**: `Integer`, **Optional** (Default: 0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 36
#### - HotbarSize (Stable)
* Number of available hotbar slots
* **Type**: `Integer`, **Optional** (Default: 3)
* **Constraint**: Value must be greater or equal than 3 and less or equal than 8
#### - OffHandSlots (Stable)
* The number of slots for off-hand items
* **Type**: `Integer`, **Optional** (Default: 0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 4
#### - HotbarItems (Stable)
* Hotbar items (e.g. primary weapon, secondary weapon, etc)
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
#### - OffHandItems (Stable)
* Off-hand items (e.g. shields, torches, etc)
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
#### - PossibleInventoryItems (Stable)
* A droplist defining the possible items the NPCs inventory could contain
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - DefaultOffHandSlot (Stable)
* The default off-hand item slot (-1 is empty)
* **Type**: `Integer`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 4
#### - Armor (WorkInProgress)
* Armor items
* **Type**: `AssetArray`, **Optional** (Default: null), **Element Type**: Item
#### - DropList (Stable)
* Drop list to spawn when killed
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - StartState (Stable)
* Initial state
* **Type**: `String`, **Optional** (Default: start)
* **Constraint**: String must be not empty
#### - DefaultSubState (Stable)
* The default sub state to reference when transitioning to a main state without a specified sub state
* **Type**: `String`, **Optional** (Default: Default)
* **Constraint**: String must be not empty
#### - CollisionDistance (Stable)
* Collision lookahead
* **Type**: `Double`, **Optional** (Default: 5.0)
* **Constraint**: Value must be greater than 0
#### - CollisionForceFalloff (Experimental)
* Falloff rate for collision force
* **Type**: `Double`, **Optional** (Default: 2.0)
* **Constraint**: Value must be greater than 0
#### - CollisionRadius (Experimental)
* Collision radius override
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - CollisionViewAngle (Experimental)
* Collision detection view cone
* **Type**: `Double`, **Optional** (Default: 320.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - SeparationDistance (Experimental)
* Desired separation distance
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SeparationWeight (Experimental)
* Blend factor separation
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - SeparationDistanceTarget (Experimental)
* Desired separation distance when close to target
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - SeparationNearRadiusTarget (Experimental)
* Distance when using SeparationDistanceTarget
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SeparationFarRadiusTarget (Experimental)
* Use normal separation distance from further than this distance
* **Type**: `Double`, **Optional** (Default: 5.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - ApplyAvoidance (Experimental)
* Apply avoidance steering force
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ApplySeparation (Experimental)
* Apply separation steering force
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - AvoidanceMode (Experimental)
* Abilities to use for avoidance
* **Type**: `Flag`, **Optional** (Default: Any)
* **Flag Values**:
* `Evade`: Only evade
* `Slowdown`: Only slow down NPC
* `Any`: Any avoidance allowed
#### - EntityAvoidanceStrength (Experimental)
* Blending factor avoidance
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - StayInEnvironment (Experimental)
* Stay in spawning environment
* **Type**: `Boolean`, **Optional** (Default: false)
#### - AllowedEnvironments (Experimental)
* Allowed environment to walk in
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - FlockSpawnTypes (WorkInProgress)
* Types of NPC this flock should consist off
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
#### - FlockSpawnTypesRandom (WorkInProgress)
* Create a randomized flock if true else spawn in order of FlockSpawnTypes
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - FlockAllowedNPC (Experimental)
* List of NPCs allowed in flock
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
#### - FlockCanLead (Experimental)
* This NPC can be flock leader
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - FlockWeightAlignment (Experimental)
* Blending flock alignment
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockWeightSeparation (Experimental)
* Blending flock separation
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockWeightCohesion (Experimental)
* Blending flock cohesion
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0
#### - FlockInfluenceRange (Experimental)
* Influence radius flock forces
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - DisableDamageFlock (WorkInProgress)
* If true disables combat damage from flock members
* **Type**: `Boolean`, **Optional** (Default: true)
#### - DisableDamageGroups (WorkInProgress)
* Members in this list of group won't cause damage
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: TagSet
#### - BusyStates (Stable)
* States during which this NPC is busy and can't be interacted with
* **Type**: `StringList`, **Required**
* **Constraint**: String must be a valid state string. A main state must be included before the period (e.g. Main.Test). State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
#### - CombatConfig (Stable)
* The combat configuration providing optional combat action evaluator
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - Invulnerable (Stable)
* Makes NPC ignore damage
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - BreathesInAir (WorkInProgress)
* Can breath in air
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BreathesInWater (WorkInProgress)
* Can breath in fluid/water
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - PickupDropOnDeath (Stable)
* Drop last picked item on death
* **Type**: `Boolean`, **Optional** (Default: false)
#### - DeathAnimationTime (Experimental)
* How long to let the death animation play before removing
* **Type**: `Double`, **Optional** (Default: 5.0)
* **Constraint**: Value must be greater or equal than 0
#### - DeathInteraction (Experimental)
* Interaction to run on death
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - DespawnAnimationTime (Experimental)
* How long to let the despawn animation play before removing
* **Type**: `Double`, **Optional** (Default: 0.800000011920929)
* **Constraint**: Value must be greater or equal than 0
#### - SpawnParticles (Experimental)
* Particle system when spawning
* **Type**: `String`, **Optional** (Default: null)
#### - SpawnParticlesOffset (Experimental)
* Displacement from foot point to spawn relative to NPC heading
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Double
#### - SpawnViewDistance (Experimental)
* View distance for spawn particle
* **Type**: `Double`, **Optional** (Default: 75.0)
* **Constraint**: Value must be greater than 0
#### - DefaultPlayerAttitude (Stable)
* The default attitude of this NPC towards players
* **Type**: `Flag`, **Optional** (Default: HOSTILE), **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
#### - DefaultNPCAttitude (Stable)
* The default attitude of this NPC towards other NPCs
* **Type**: `Flag`, **Optional** (Default: NEUTRAL), **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
#### - AttitudeGroup (Stable)
* The attitude group towards other NPCs this NPC belongs to (often species related)
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - ItemAttitudeGroup (Stable)
* This NPC's item attitudes
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - CorpseStaysInFlock (Stable)
* Whether the NPC should stay in the flock until corpse removal or be removed at the moment of death
* **Type**: `Boolean`, **Optional** (Default: false)
#### - OverrideHeadPitchAngle (Experimental)
* Whether to override the head pitch angle range
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - HeadPitchAngleRange (Experimental)
* Head rotation pitch range to be used instead of model camera settings
* **Type**: `Array`, **Optional** (Default: \[-89.0, 89.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -90, less or equal than 90and in weakly ascending order
#### - MotionControllerList (Stable)
* Motion controllers
* **Type**: `ObjectRef`, **Required**, **Object Type**: HashMap
#### - Instructions (WorkInProgress)
* List of instructions
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Instruction
#### - InteractionInstruction (Stable)
* An instruction designed to evaluate and set which players can interact with an NPC, along with setting correct states upon interaction
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Instruction
#### - DeathInstruction (Stable)
* An instruction which will run only when the NPC is dead until they are removed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Instruction
#### - StateTransitions (Stable)
* A set of state transitions and the actions that will be executed during them
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: StateTransitionController
#### - StateEvaluator (Stable)
* A state evaluator
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - InitialMotionController (Stable)
* The initial motion controller to set. If omitted and there are multiple, one will be chosen at random.
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - InteractionVars (Stable)
* Interaction vars to be used in interactions.
* **Type**: `CodecObject`, **Optional** (Default: null)
#### - IsMemory (Stable)
* Used to define if the NPC has a Memory to record.
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - MemoriesCategory (Stable)
* Category to put the NPC in, as part of the Memories Plugin
* **Type**: `String`, **Optional** (Default: Other), **Computable**
* **Constraint**: String value must be either null or not empty
#### - MemoriesNameOverride (Stable)
* Overrides the Memory name when set.
* **Type**: `String`, **Optional** (Default: ), **Computable**
#### - SpawnLockTime (Stable)
* How long the NPC should be locked and unable to perform behavior when first spawned
* **Type**: `Double`, **Optional** (Default: 1.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
### Constraints
* At least one of BreathesInAir, BreathesInWater must be true
[(Top)](#top)
## Variant: Role (WorkInProgress)
Create a variant from an existing NPC JSON file
[(Top)](#top)
# List of Motion Controllers
## Dive: MotionController (WorkInProgress)
Provide diving abilities for NPC
### Attributes
#### - Type (Stable)
* Type field
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
#### - EpsilonSpeed (Experimental)
* Minimum speed considered non 0
* **Type**: `Double`, **Optional** (Default: 1.0E-5)
* **Constraint**: Value must be greater than 0
#### - EpsilonAngle (Experimental)
* Minimum angle difference considered non 0 in degrees
* **Type**: `Double`, **Optional** (Default: 3.0)
* **Constraint**: Value must be greater than 0
#### - MaxHeadRotationSpeed (Stable)
* Maximum rotation speed of the head in degrees
* **Type**: `Double`, **Optional** (Default: 360.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - ForceVelocityDamping (Experimental)
* Damping of external force/velocity over time
* **Type**: `Double`, **Optional** (Default: 0.5)
* **Constraint**: Value must be greater than 0
#### - RunThreshold (WorkInProgress)
* Relative threshold when running animation should be used
* **Type**: `Double`, **Optional** (Default: 0.7), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - RunThresholdRange (WorkInProgress)
* Relative threshold range for switching between running/walking
* **Type**: `Double`, **Optional** (Default: 0.15)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MaxSwimSpeed (Stable)
* Maximum horizontal speed
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxDiveSpeed (Stable)
* Maximum vertical speed
* **Type**: `Double`, **Optional** (Default: 8.0)
* **Constraint**: Value must be greater than 0
#### - MaxFallSpeed (Stable)
* Terminal velocity falling in air
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - MaxSinkSpeed (Stable)
* Terminal velocity sinking in water
* **Type**: `Double`, **Optional** (Default: 4.0)
* **Constraint**: Value must be greater than 0
#### - Gravity (Stable)
* Gravity
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - Acceleration (Stable)
* Acceleration
* **Type**: `Double`, **Optional** (Default: 3.0)
* **Constraint**: Value must be greater than 0
#### - MaxRotationSpeed (Stable)
* Maximum rotational speed in degrees
* **Type**: `Double`, **Optional** (Default: 360.0)
* **Constraint**: Value must be greater than 0
#### - MaxSwimTurnAngle (WorkInProgress)
* Maximum angle NPC can walk without explicit turning in degrees
* **Type**: `Double`, **Optional** (Default: 90.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - FastSwimThreshold (WorkInProgress)
* Relative threshold when fast swimming animation should be used
* **Type**: `Double`, **Optional** (Default: 0.6)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - SwimDepth (WorkInProgress)
* 0 is at eye height, -1 is bottom of bounding box, +1 top of bounding box. other values between -1 and +1 scale linear
* **Type**: `Double`, **Optional** (Default: 0.4)
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - SinkRatio (WorkInProgress)
* Relative sink or climb speed while wandering
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinDiveDepth (Unknown)
* null
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MaxDiveDepth (WorkInProgress)
* Maximum dive depth below surface desired
* **Type**: `Double`, **Optional** (Default: 1.7976931348623157E308)
* **Constraint**: Value must be greater than 0
#### - MinDepthAboveGround (WorkInProgress)
* Minimum distance from ground desired
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinDepthBelowSurface (WorkInProgress)
* Minimum distance from water surface desired
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinWaterDepth (Unknown)
* null
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0
#### - MaxWaterDepth (Unknown)
* null
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - DesiredDepthWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast.
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
### Constraints
* MinSwimSpeed must be less or equal than MaxSwimSpeed
[(Top)](#top)
## Fly: MotionController (WorkInProgress)
Flight motion controller
### Attributes
#### - Type (Stable)
* Type field
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
#### - EpsilonSpeed (Experimental)
* Minimum speed considered non 0
* **Type**: `Double`, **Optional** (Default: 1.0E-5)
* **Constraint**: Value must be greater than 0
#### - EpsilonAngle (Experimental)
* Minimum angle difference considered non 0 in degrees
* **Type**: `Double`, **Optional** (Default: 3.0)
* **Constraint**: Value must be greater than 0
#### - MaxHeadRotationSpeed (Stable)
* Maximum rotation speed of the head in degrees
* **Type**: `Double`, **Optional** (Default: 360.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - ForceVelocityDamping (Experimental)
* Damping of external force/velocity over time
* **Type**: `Double`, **Optional** (Default: 0.5)
* **Constraint**: Value must be greater than 0
#### - RunThreshold (WorkInProgress)
* Relative threshold when running animation should be used
* **Type**: `Double`, **Optional** (Default: 0.7), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - RunThresholdRange (WorkInProgress)
* Relative threshold range for switching between running/walking
* **Type**: `Double`, **Optional** (Default: 0.15)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MinAirSpeed (WorkInProgress)
* Minimum in air speed
* **Type**: `Double`, **Optional** (Default: 0.1)
* **Constraint**: Value must be greater or equal than 0
#### - MaxHorizontalSpeed (Stable)
* Maximum horizontal speed
* **Type**: `Double`, **Optional** (Default: 8.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxClimbSpeed (Stable)
* Maximum climbing speed
* **Type**: `Double`, **Optional** (Default: 6.0)
* **Constraint**: Value must be greater than 0
#### - MaxSinkSpeed (Stable)
* Maximum sink/drop speed
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - MaxFallSpeed (Stable)
* Maximum fall speed
* **Type**: `Double`, **Optional** (Default: 40.0)
* **Constraint**: Value must be greater than 0
#### - MaxSinkSpeedFluid (Stable)
* Maximum sink/fall speed in fluids
* **Type**: `Double`, **Optional** (Default: 4.0)
* **Constraint**: Value must be greater or equal than 0
#### - MaxClimbAngle (Stable)
* Maximum climb angle
* **Type**: `Double`, **Optional** (Default: 45.0)
* **Constraint**: Value must be greater than 0
#### - MaxSinkAngle (Stable)
* Maximum sink angle
* **Type**: `Double`, **Optional** (Default: 85.0)
* **Constraint**: Value must be greater than 0
#### - Acceleration (Stable)
* Maximum Acceleration
* **Type**: `Double`, **Optional** (Default: 4.0)
* **Constraint**: Value must be greater than 0
#### - Deceleration (Stable)
* Maximum deceleration
* **Type**: `Double`, **Optional** (Default: 4.0)
* **Constraint**: Value must be greater than 0
#### - Gravity (Stable)
* Gravity
* **Type**: `Double`, **Optional** (Default: 40.0)
* **Constraint**: Value must be greater than 0
#### - MaxTurnSpeed (Stable)
* Maximum turn speed in degrees per second
* **Type**: `Double`, **Optional** (Default: 180.0)
* **Constraint**: Value must be greater than 0
#### - MaxRollAngle (Stable)
* Maximum roll angle in degrees
* **Type**: `Double`, **Optional** (Default: 45.0)
* **Constraint**: Value must be greater than 0
#### - MaxRollSpeed (Stable)
* Maximum roll speed in degrees per second
* **Type**: `Double`, **Optional** (Default: 180.0)
* **Constraint**: Value must be greater than 0
#### - RollDamping (Stable)
* Roll damping
* **Type**: `Double`, **Optional** (Default: 0.8999999761581421)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MinHeightOverGround (Stable)
* Minimum height over ground
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxHeightOverGround (Stable)
* Maximum height over ground
* **Type**: `Double`, **Optional** (Default: 20.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - FastFlyThreshold (WorkInProgress)
* Relative threshold when fast flying animation should be used
* **Type**: `Double`, **Optional** (Default: 0.6)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - AutoLevel (Stable)
* Set pitch to 0 when no steering forces applied
* **Type**: `Boolean`, **Optional** (Default: true)
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast.
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
[(Top)](#top)
## Walk: MotionController (WorkInProgress)
Provide walk on ground abilities for NPC
### Attributes
#### - Type (Stable)
* Type field
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
#### - EpsilonSpeed (Experimental)
* Minimum speed considered non 0
* **Type**: `Double`, **Optional** (Default: 1.0E-5)
* **Constraint**: Value must be greater than 0
#### - EpsilonAngle (Experimental)
* Minimum angle difference considered non 0 in degrees
* **Type**: `Double`, **Optional** (Default: 3.0)
* **Constraint**: Value must be greater than 0
#### - MaxHeadRotationSpeed (Stable)
* Maximum rotation speed of the head in degrees
* **Type**: `Double`, **Optional** (Default: 360.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - ForceVelocityDamping (Experimental)
* Damping of external force/velocity over time
* **Type**: `Double`, **Optional** (Default: 0.5)
* **Constraint**: Value must be greater than 0
#### - RunThreshold (WorkInProgress)
* Relative threshold when running animation should be used
* **Type**: `Double`, **Optional** (Default: 0.7), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - RunThresholdRange (WorkInProgress)
* Relative threshold range for switching between running/walking
* **Type**: `Double`, **Optional** (Default: 0.15)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MaxWalkSpeed (Stable)
* Maximum horizontal speed
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinWalkSpeed (WorkInProgress)
* Minimum horizontal speed
* **Type**: `Double`, **Optional** (Default: 0.1)
* **Constraint**: Value must be greater or equal than 0
#### - MaxFallSpeed (Stable)
* Maximum fall speed
* **Type**: `Double`, **Optional** (Default: 8.0)
* **Constraint**: Value must be greater than 0
#### - MaxSinkSpeedFluid (Stable)
* Maximum sink speed in fluids
* **Type**: `Double`, **Optional** (Default: 4.0)
* **Constraint**: Value must be greater than 0
#### - Gravity (Stable)
* Gravity
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - Acceleration (Stable)
* Acceleration
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxRotationSpeed (Stable)
* Maximum rotational speed in degrees
* **Type**: `Double`, **Optional** (Default: 360.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxWalkTurnAngle (WorkInProgress)
* Maximum angle NPC can walk without explicit turning in degrees
* **Type**: `Double`, **Optional** (Default: 90.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - BlendRestTurnAngle (WorkInProgress)
* When NPC is blending heading and turn angle required is larger than this value speed is reduced
* **Type**: `Double`, **Optional** (Default: 60.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - BlendRestRelativeSpeed (WorkInProgress)
* When NPC is blending heading relative speed used when reducing speed
* **Type**: `Double`, **Optional** (Default: 0.2), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MaxClimbHeight (Stable)
* Maximum height NPC can climb
* **Type**: `Double`, **Optional** (Default: 1.3), **Computable**
* **Constraint**: Value must be greater than 0
#### - JumpHeight (Experimental)
* How high the NPC jumps above climb height
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MinJumpHeight (Experimental)
* Minimum height above which a jump will be attempted
* **Type**: `Double`, **Optional** (Default: 0.6), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MinJumpDistance (Experimental)
* Minimum distance above which a jump will be executed
* **Type**: `Double`, **Optional** (Default: 0.2), **Computable**
* **Constraint**: Value must be greater than 0
#### - JumpForce (Experimental)
* The force multiplier for the upward motion of the jump
* **Type**: `Double`, **Optional** (Default: 1.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - JumpBlending (Experimental)
* The blending of the upward jump pattern. 0 is more curved and 1 is linear
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - JumpDescentBlending (Experimental)
* The blending of the jump descent pattern. 0 is linear while higher values become more curved
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - JumpDescentSteepness (Experimental)
* The steepness of the descent portion of the jump
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AscentAnimationType (Stable)
* The animation to play when walking up a block
* **Type**: `Flag`, **Optional** (Default: Walk), **Computable**
* **Flag Values**:
* `Walk`: Play walk animation
* `Fly`: Play fly animation
* `Idle`: Play idle animation
* `Climb`: Play climb animation
* `Jump`: Play jump animation
#### - ClimbSpeedMult (WorkInProgress)
* Climb speed multiplier (const + multiplier \* walkspeed \*\* power)
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
#### - ClimbSpeedPow (WorkInProgress)
* Climb speed power (const + multiplier \* walkspeed \*\* power)
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
#### - ClimbSpeedConst (WorkInProgress)
* Climb speed constant (const + multiplier \* walkspeed \*\* power)
* **Type**: `Double`, **Optional** (Default: 5.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinDescentAnimationHeight (Stable)
* The min drop distance to switch from the standard walking animation to the specified descent animation
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - DescendFlatness (WorkInProgress)
* Relative scale how fast NPC moves forward while climbing down
* **Type**: `Double`, **Optional** (Default: 0.7), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - DescendSpeedCompensation (WorkInProgress)
* Factor to compensate forward speed reduction while moving downwards
* **Type**: `Double`, **Optional** (Default: 0.9), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - DescentAnimationType (Experimental)
* The animation to play when moving down a block
* **Type**: `Flag`, **Optional** (Default: Fall), **Computable**
* **Flag Values**:
* `Walk`: Play walk animation
* `Idle`: Play idle animation
* `Fall`: Play fall animation
#### - DescentSteepness (Experimental)
* The relative steepness of the descent
* **Type**: `Double`, **Optional** (Default: 1.4), **Computable**
* **Constraint**: Value must be greater than 0
#### - DescentBlending (Experimental)
* The blending of the descent pattern. 0 is linear, while higher values become more curved
* **Type**: `Double`, **Optional** (Default: 1.8), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxDropHeight (WorkInProgress)
* Maximum height NPC considers drop safe
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - FenceBlockSet (Stable)
* Unclimbable blocks
* **Type**: `Asset`, **Optional** (Default: Fence)
#### - JumpRange (WorkInProgress)
* Jump distance range
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 10and in weakly ascending order
#### - MinHover (WorkInProgress)
* Minimum hover height over ground
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinHoverClimb (WorkInProgress)
* Minimum hover height over ground when climbing
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinHoverDrop (WorkInProgress)
* Minimum hover height over ground when dropping
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - FloatsDown (WorkInProgress)
* If true NPC floats down when hovering enabled else gravity decides
* **Type**: `Boolean`, **Optional** (Default: true)
#### - MaxHover (WorkInProgress)
* Maximum hover height over ground
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - HoverFreq (WorkInProgress)
* Hover frequency
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MinHitSlowdown (Stable)
* The minimum percentage to slow down by when attacked from behind
* **Type**: `Double`, **Optional** (Default: 0.1)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
### Constraints
* MinHover must be less or equal than MaxHover
* MinHoverClimb must be less or equal than MinHover
* MinHoverDrop must be less or equal than MinHover
* MinWalkSpeed must be less or equal than MaxWalkSpeed
[(Top)](#top)
# List of Misc Parts
## ActionList: ActionList (Stable)
An array of actions to be executed
* List of actions
* **Type**: `Array`, **Required**, **Element Type**: Action
[(Top)](#top)
## Aim: HeadMotion (Stable)
Aim at target considering weapon in hand.
### Attributes
#### - Spread (Experimental)
* Random targeting error
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 5
#### - HitProbability (Experimental)
* Probability of shot being straight on target
* **Type**: `Double`, **Optional** (Default: 0.33), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - Deflection (Experimental)
* Compute deflection for moving targets
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelativeTurnSpeed (Stable)
* The relative turn speed modifier
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## AimCharge: BodyMotion (Stable)
Aim the NPC at a target position for performing a charge based on aiming information and ensure that the charge is possible before it's executed.
### Attributes
#### - RelativeTurnSpeed (Stable)
* The relative turn speed modifier
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## Altitude: IEntityFilter (Stable)
Matches targets if they're within the defined range above the ground
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AltitudeRange (Stable)
* The range above the ground to match
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
[(Top)](#top)
## And: IEntityFilter (Stable)
Logical AND of a list of filters
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Filters (Stable)
* List of filters
* **Type**: `Array`, **Required**, **Element Type**: IEntityFilter
* **Constraint**: Array must not be empty
[(Top)](#top)
## Attitude: ISensorEntityPrioritiser (Stable)
Prioritises return entities by attitude
### Attributes
#### - AttitudesByPriority (Stable)
* A prioritised list of attitudes
* **Type**: `FlagArray`, **Required**, **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
* **Constraint**: Array must not contain duplicates
[(Top)](#top)
## Attitude: IEntityFilter (Stable)
Matches the attitude towards the locked target
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Attitudes (Stable)
* The attitudes to match
* **Type**: `FlagSet`, **Required**, **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
### Constraints
[(Top)](#top)
## Combat: IEntityFilter (Stable)
Check the target's combat state. This includes whether they're attacking at all, if they're using a particular attack, etc
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sequence (Stable)
* The attack ID to check for.
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - TimeElapsedRange (Stable)
* The acceptable elapsed time range in seconds.
* **Type**: `Array`, **Optional** (Default: \[0.0, 3.4028234663852886E38]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 3.4028234663852886e+38and in weakly ascending order
#### - Mode (Stable)
* Type of combat to trigger on
* **Type**: `Flag`, **Optional** (Default: Attacking), **Computable**
* **Flag Values**:
* `Ranged`: Ranged
* `Charging`: Weapon charging
* `Melee`: Melee
* `Blocking`: Blocking
* `Sequence`: Combat sequence
* `Attacking`: Attacking
* `Any`: Any
* `None`: None
### Constraints
* If Mode is Sequence, the asset Sequencemust be provided
[(Top)](#top)
## CombatTargets: ISensorEntityCollector (Stable)
A collector which processes matched friendly and hostile targets and adds them to the NPC's short-term combat memory.
[(Top)](#top)
## Flee: BodyMotion (Experimental)
Move away from a target using path finding or steering
### Attributes
#### - RelativeSpeed (Stable)
* Maximum relative speed the NPC should move
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - RelativeSpeedWaypoint (Stable)
* Maximum relative speed the NPC should move close to waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - WaypointRadius (Stable)
* Radius to slow down around waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0.1
#### - UseBestPath (Stable)
* Use best partial path if goal can't be reached
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ThrottleDelayRange (Stable)
* Time to delay after no path finding solution found
* **Type**: `Array`, **Optional** (Default: \[3.0, 5.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - ThrottleIgnoreCount (Stable)
* How often no valid path solution can be found before throttling delay is applied
* **Type**: `Integer`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - BuildOptimisedPath (Stable)
* Try to reduce number of nodes of generated path
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlendHeading (Stable)
* Relative rotation angle into next waypoint when arriving at current waypoint
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - PathSmoothing (Stable)
* Try to smooth followed path. Larger values smooth more.
* **Type**: `Integer`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - RejectionWeight (Stable)
* Weight of rejection vector pushing entity closer to original path
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - UseSteering (Stable)
* Use simple/cheap steering if available
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - SkipSteering (Experimental)
* Skip steering if target not reachable
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - UsePathfinder (Stable)
* Use path finder
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MinPathLength (Experimental)
* Minimum length of path required when not able to reach target (should be greater equal 2)
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - DiagonalMoves (Stable)
* Allow diagonal moves
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - StepsPerTick (Stable)
* Steps per iteration
* **Type**: `Integer`, **Optional** (Default: 50.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxPathLength (Stable)
* Max path steps before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxOpenNodes (Stable)
* Max open nodes before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxTotalNodes (Stable)
* Max total node before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 900.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (Stable)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - WaitDistance (Experimental)
* Minimum distance target needs to move before recomputing path when no path can be found
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - RecomputeDistance (Experimental)
* Maximum distance target can move before path is recomputed or 0 to supress recomputation
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - ReprojectDistance (Experimental)
* Maximum distance target can move before position is reprojected
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - AdjustRangeByHitboxSize (Stable)
* Correct range by hitbox sizes of involved entities
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - RecomputeConeAngle (Experimental)
* Recompute path when target leaves cone from initial position to target
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - SlowDownDistance (Stable)
* Distance from target when NPC should start to slowdown
* **Type**: `Double`, **Optional** (Default: 8.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance from target when NPC should halt
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Falloff (Stable)
* Rate how fast the slowdown should happen relative to distance
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - HoldDirectionTimeRange (Stable)
* How often to change heading
* **Type**: `Array`, **Optional** (Default: \[2.0, 5.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - ChangeDirectionViewSector (Stable)
* The view sector the NPC uses to decide if it should switch direction
* **Type**: `Double`, **Optional** (Default: 230.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - DirectionJitter (Stable)
* How much jitter in degrees to add to the heading the NPC uses
* **Type**: `Double`, **Optional** (Default: 45.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - ErraticDistance (Stable)
* If the player is closer than this distance, the NPC will behave more erratically using the additional jitter parameter
* **Type**: `Double`, **Optional** (Default: 4.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - ErraticExtraJitter (Stable)
* Extra jitter to add to the NPC heading on top of the standard when the target is too close
* **Type**: `Double`, **Optional** (Default: 45.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - ErraticChangeDurationMultiplier (Stable)
* A multiplier to decrease the duration between direction changes when the target is too close
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
* SlowDownDistance must be less or equal than StopDistance
[(Top)](#top)
## Flock: BodyMotion (Experimental)
Flocking - WIP
### Constraints
[(Top)](#top)
## Flock: IEntityFilter (Stable)
Test for flock membership and related properties
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - FlockStatus (Stable)
* Test for NPC status in relation to flock
* **Type**: `Flag`, **Optional** (Default: Any)
* **Flag Values**:
* `Leader`: Is leader of a flock
* `Follower`: Is part of a flock but not leader
* `NotMember`: Is not part of a flock
* `Member`: Is part of a flock
* `Any`: Don't care
#### - FlockPlayerStatus (Stable)
* Test for Player status for flock NPC is member
* **Type**: `Flag`, **Optional** (Default: Any)
* **Flag Values**:
* `NotMember`: Player is not member of a flock
* `Member`: Player is member of a flock
* `Any`: Don't care
#### - Size (Stable)
* Check for a certain range of NPCs in the flock
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: integer
#### - CheckCanJoin (Stable)
* If true, will filter entities in a flock the executor can join
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## HashMap: HashMap (Stable)
Non-empty list of motion controllers.
* List of motion controllers
* **Type**: `Array`, **Required**, **Element Type**: MotionController
* **Constraint**: Array must not be empty
[(Top)](#top)
## HeightDifference: IEntityFilter (Stable)
Matches entities within the given height range
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - HeightDifference (Stable)
* The height range to test entities in. Extends into negatives for positions below the NPC
* **Type**: `Array`, **Optional** (Default: \[-1.7976931348623157E308, 1.7976931348623157E308]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -1.7976931348623157e+308, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - UseEyePosition (Stable)
* Use eye position for height difference checks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
[(Top)](#top)
## InsideBlock: IEntityFilter (Stable)
Matches if the entity is inside any of the blocks in the BlockSet
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlockSet (Stable)
* The BlockSet to match against
* **Type**: `Asset`, **Required**, **Computable**
[(Top)](#top)
## Instruction: Instruction (WorkInProgress)
An instruction with Sensor, and Motions and Actions, or a list of nested instructions.
### Attributes
#### - Name (Stable)
* Optional name for descriptor
* **Type**: `String`, **Optional** (Default: null)
#### - Tag (Experimental)
* Internal identifier tag for debugging
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - Enabled (Stable)
* Whether this instruction should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor for testing if instruction can be applied. If not supplied, will always match
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Sensor
#### - BodyMotion (Stable)
* Body motion to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: BodyMotion
#### - HeadMotion (Stable)
* Head motion to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: HeadMotion
#### - Actions (Stable)
* Actions to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ActionList
#### - ActionsBlocking (Stable)
* Do not execute an action unless the previous action could execute
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ActionsAtomic (Stable)
* Only execute actions if all actions can be executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Instructions (Stable)
* Optional nested list of instructions
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Instruction
#### - Continue (WorkInProgress)
* Continue after this instruction was executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Weight (Stable)
* Weighted chance of picking this instruction in a random instruction
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - TreeMode (Stable)
* Whether this instruction and its contents should be treated like a traditional behaviour tree, i.e. will continue if all child instructions fail
* **Type**: `Boolean`, **Optional** (Default: false)
#### - InvertTreeModeResult (Stable)
* Whether or not to invert the result of TreeMode evaluation when passing up to parent TreeMode instructions
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Constraints
* At most one or none of BodyMotion, Instructions must be provided
* At most one or none of HeadMotion, Instructions must be provided
* At most one or none of Actions, Instructions must be provided
* If TreeMode is true, Continue must be false
[(Top)](#top)
## Inventory: IEntityFilter (Stable)
Test various conditions relating to entity inventory. This includes whether it contains a specific item, item count, and free slots
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Items (Stable)
* A list of glob item patterns to match
* **Type**: `AssetArray`, **Optional** (Default: \[\*]), **Computable**, **Element Type**: Item
#### - CountRange (Stable)
* The range of number of items that need to match the patterns
* **Type**: `Array`, **Optional** (Default: \[1, 2147483647]), **Computable**, **Element Type**: integer
* **Constraint**: Values must be greater or equal than 0, less or equal than 2147483647and in weakly ascending order
#### - FreeSlotRange (Stable)
* The range designating the number of required free slots. Setting min and max to zero would check if full
* **Type**: `Array`, **Optional** (Default: \[0, 2147483647]), **Computable**, **Element Type**: integer
* **Constraint**: Values must be greater or equal than 0, less or equal than 2147483647and in weakly ascending order
[(Top)](#top)
## ItemInHand: IEntityFilter (Stable)
Check if entity is holding an item
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Items (Stable)
* A list of glob item patterns to match
* **Type**: `AssetArray`, **Required**, **Computable**, **Element Type**: Item
#### - Hand (Stable)
* Which hand to check
* **Type**: `Flag`, **Optional** (Default: Both), **Computable**
* **Flag Values**:
* `OffHand`: The off-hand
* `Main`: The main hand
* `Both`: Both hands
[(Top)](#top)
## Land: BodyMotion (Experimental)
Try to land at the given position using a seek like motion
### Attributes
#### - RelativeSpeed (Stable)
* Maximum relative speed the NPC should move
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - RelativeSpeedWaypoint (Stable)
* Maximum relative speed the NPC should move close to waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - WaypointRadius (Stable)
* Radius to slow down around waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0.1
#### - UseBestPath (Stable)
* Use best partial path if goal can't be reached
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ThrottleDelayRange (Stable)
* Time to delay after no path finding solution found
* **Type**: `Array`, **Optional** (Default: \[3.0, 5.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - ThrottleIgnoreCount (Stable)
* How often no valid path solution can be found before throttling delay is applied
* **Type**: `Integer`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - BuildOptimisedPath (Stable)
* Try to reduce number of nodes of generated path
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlendHeading (Stable)
* Relative rotation angle into next waypoint when arriving at current waypoint
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - PathSmoothing (Stable)
* Try to smooth followed path. Larger values smooth more.
* **Type**: `Integer`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - RejectionWeight (Stable)
* Weight of rejection vector pushing entity closer to original path
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - UseSteering (Stable)
* Use simple/cheap steering if available
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - SkipSteering (Experimental)
* Skip steering if target not reachable
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - UsePathfinder (Stable)
* Use path finder
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MinPathLength (Experimental)
* Minimum length of path required when not able to reach target (should be greater equal 2)
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - DiagonalMoves (Stable)
* Allow diagonal moves
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - StepsPerTick (Stable)
* Steps per iteration
* **Type**: `Integer`, **Optional** (Default: 50.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxPathLength (Stable)
* Max path steps before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxOpenNodes (Stable)
* Max open nodes before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxTotalNodes (Stable)
* Max total node before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 900.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (Stable)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - WaitDistance (Experimental)
* Minimum distance target needs to move before recomputing path when no path can be found
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - RecomputeDistance (Experimental)
* Maximum distance target can move before path is recomputed or 0 to supress recomputation
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - ReprojectDistance (Experimental)
* Maximum distance target can move before position is reprojected
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - AdjustRangeByHitboxSize (Stable)
* Correct range by hitbox sizes of involved entities
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - RecomputeConeAngle (Experimental)
* Recompute path when target leaves cone from initial position to target
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - Reachable (Experimental)
* Target must be reachable so that hitboxes can overlap
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - HeightDifference (Experimental)
* Height difference allowed to target
* **Type**: `Array`, **Optional** (Default: \[-1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -1.7976931348623157e+308, less or equal than 1.7976931348623157e+308and in strictly ascending order
#### - SlowDownDistance (Stable)
* Distance when to slow down when approaching
* **Type**: `Double`, **Optional** (Default: 8.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance to stop at
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AbortDistance (Stable)
* Distance to abort behaviour
* **Type**: `Double`, **Optional** (Default: 96.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Falloff (Stable)
* Deceleration when approaching target
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SwitchToSteeringDistance (Stable)
* Distance below NPC can test if target is reachable and abort existing path
* **Type**: `Double`, **Optional** (Default: 20.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - GoalLenience (Experimental)
* The distance from the target landing point that is acceptable to land at
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater than 0
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
* SlowDownDistance must be greater or equal than StopDistance
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## Leave: BodyMotion (Experimental)
Get away from current position using path finding
### Attributes
#### - RelativeSpeed (Stable)
* Maximum relative speed the NPC should move
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - RelativeSpeedWaypoint (Stable)
* Maximum relative speed the NPC should move close to waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - WaypointRadius (Stable)
* Radius to slow down around waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0.1
#### - UseBestPath (Stable)
* Use best partial path if goal can't be reached
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ThrottleDelayRange (Stable)
* Time to delay after no path finding solution found
* **Type**: `Array`, **Optional** (Default: \[3.0, 5.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - ThrottleIgnoreCount (Stable)
* How often no valid path solution can be found before throttling delay is applied
* **Type**: `Integer`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - BuildOptimisedPath (Stable)
* Try to reduce number of nodes of generated path
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlendHeading (Stable)
* Relative rotation angle into next waypoint when arriving at current waypoint
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - PathSmoothing (Stable)
* Try to smooth followed path. Larger values smooth more.
* **Type**: `Integer`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - RejectionWeight (Stable)
* Weight of rejection vector pushing entity closer to original path
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinPathLength (Experimental)
* Minimum length of path required when not able to reach target (should be greater equal 2)
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - DiagonalMoves (Stable)
* Allow diagonal moves
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - StepsPerTick (Stable)
* Steps per iteration
* **Type**: `Integer`, **Optional** (Default: 50.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxPathLength (Stable)
* Max path steps before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxOpenNodes (Stable)
* Max open nodes before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxTotalNodes (Stable)
* Max total node before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 900.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (Stable)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - Distance (Experimental)
* Minimum distance required
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
### Constraints
[(Top)](#top)
## LineOfSight: IEntityFilter (Stable)
Matches if there is line of sight to the target
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
[(Top)](#top)
## MaintainDistance: BodyMotion (Stable)
Maintain distance from a given position
### Attributes
#### - DesiredDistanceRange (Stable)
* The desired distance to remain at.
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -1.7976931348623157e+308, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - TargetDistanceFactor (Stable)
* A factor used to decide what distance to move to within the target range when the target falls outside of it. 0 will result in moving the shortest distance to fall within the range, 1 the furthest distance, and 0.5 roughly the middle of the range.
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - MoveThreshold (Stable)
* An extra threshold distance on either side of the desired range before the NPC will trigger movement.
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - RelativeForwardsSpeed (Stable)
* Maximum relative speed for the NPC moving forwards
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - RelativeBackwardsSpeed (Stable)
* Maximum relative speed for the NPC moving backwards
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - MoveTowardsSlowdownThreshold (Stable)
* The distance away from the target stopping point at which the NPC will start to slow down while moving towards the target
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - StrafingDurationRange (Stable)
* How long to strafe for (moving left or right around the target). If set to \[ 0, 0 ], will not move horizontally at all.
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - StrafingFrequencyRange (Stable)
* How frequently to execute strafing
* **Type**: `Array`, **Optional** (Default: \[2.0, 2.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## MatchLook: BodyMotion (Stable)
Make NPC body rotate to match look direction
### Constraints
[(Top)](#top)
## MovementState: IEntityFilter (Stable)
Check if the entity is in the given movement state
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - State (Stable)
* The movement state to check
* **Type**: `Flag`, **Required**
* **Flag Values**:
* `WALKING`: Walking
* `FLYING`: Flying
* `RUNNING`: Running
* `FALLING`: Falling
* `IDLE`: Idle
* `SPRINTING`: Sprinting
* `CROUCHING`: Crouching
* `CLIMBING`: Climbing
* `ANY`: Any
* `JUMPING`: Jumping
[(Top)](#top)
## NPCGroup: IEntityFilter (Stable)
Returns whether the entity matches one of the provided NPCGroups
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - IncludeGroups (Stable)
* A set of NPCGroups to include in the match
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: TagSet
#### - ExcludeGroups (Stable)
* A set of NPCGroups to exclude from the match
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: TagSet
### Constraints
* One (and only one) of IncludeGroups, ExcludeGroups must be provided
[(Top)](#top)
## Not: IEntityFilter (WorkInProgress)
Invert filter test
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Filter (Stable)
* Filter to test
* **Type**: `ObjectRef`, **Required**, **Object Type**: IEntityFilter
[(Top)](#top)
## Nothing: BodyMotion (Stable)
Do nothing
### Constraints
[(Top)](#top)
## Nothing: HeadMotion (Stable)
Do nothing
### Constraints
[(Top)](#top)
## Observe: HeadMotion (Stable)
Observe surroundings in various ways. This includes looking in random directions within an angle, or sweeping the head left and right, etc. Angles are relative to body angle at any given time.
### Attributes
#### - AngleRange (Stable)
* The angle range to observe in degrees, offset from facing forwards
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -180, less or equal than 180and in weakly ascending order
#### - PauseTimeRange (Stable)
* How long to continue looking in a given direction
* **Type**: `Array`, **Optional** (Default: \[2.0, 2.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - PickRandomAngle (Stable)
* Whether to pick random angles within the range. If false, will instead sweep across the range, pausing at either end.
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - ViewSegments (Stable)
* The number of distinct segments to stop at when sweeping from left to right
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - RelativeTurnSpeed (Stable)
* The relative turn speed modifier
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
### Constraints
[(Top)](#top)
## Or: IEntityFilter (Stable)
Logical OR of a list of filters
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Filters (Stable)
* List of filters
* **Type**: `Array`, **Required**, **Element Type**: IEntityFilter
* **Constraint**: Array must not be empty
[(Top)](#top)
## Path: Path (Stable)
List of transient path points
### Attributes
#### - Waypoints (Stable)
* List of transient path points
* **Type**: `Array`, **Required**, **Element Type**: RelativeWaypointDefinition
#### - Scale (Stable)
* Overall path scale
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
[(Top)](#top)
## Path: BodyMotion (Stable)
Walk along a path.
### Attributes
#### - StartAtNearestNode (Stable)
* Start at closest warp point
* **Type**: `Boolean`, **Optional** (Default: true)
#### - PathWidth (Experimental)
* Walking corridor width
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - NodeWidth (Experimental)
* Radius of warp point node
* **Type**: `Double`, **Optional** (Default: 0.2)
* **Constraint**: Value must be greater than 0
#### - MinRelSpeed (Stable)
* Minimum relative walk speed
* **Type**: `Double`, **Optional** (Default: 0.5)
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - MaxRelSpeed (Stable)
* Maximum relative walk speed
* **Type**: `Double`, **Optional** (Default: 0.5)
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - MinWalkDistance (Experimental)
* Minimum walk distance when PathWidth greater 0
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MaxWalkDistance (Experimental)
* Maximum walk distance when PathWidth greater 0
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - Direction (Stable)
* Walking direction relative to order of nodes
* **Type**: `Flag`, **Optional** (Default: FORWARD)
* **Flag Values**:
* `BACKWARD`: Start visiting nodes in reverse order
* `RANDOM`: Can change direction between nodes and randomly pick target node in Points shape mode
* `FORWARD`: Start visiting nodes in order
* `ANY`: Pick any start direction
#### - Shape (Stable)
* Shape of Path
* **Type**: `Flag`, **Optional** (Default: LOOP), **Computable**
* **Flag Values**:
* `CHAIN`: Nodes form an open path of line segments and will chain together with the next nearest path upon reaching the final node
* `LINE`: Nodes form an open path of line segments
* `LOOP`: Nodes form a closed loop of line segments (last node leads to first node)
* `POINTS`: Any path between nodes is possible
#### - MinNodeDelay (Stable)
* Minimum resting time at a node
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - MaxNodeDelay (Stable)
* Maximum resting time at a node
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
#### - UseNodeViewDirection (Stable)
* Look into next node direction at node
* **Type**: `Boolean`, **Optional** (Default: false)
#### - NodePauseScaleRange (Stable)
* The range from which to pick a value that defines the portion of the total node pause time that should be spent facing a direction before turning
* **Type**: `Array`, **Optional** (Default: \[0.2, 0.4]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - NodePauseExtraPercentRange (Stable)
* A range from which to pick the additional percentage of the directional pause time to add to it
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.2]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1and in weakly ascending order
#### - PickRandomAngle (Stable)
* Whether to sweep left and right using the observation angle, or pick a random angle within the sector each time
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ViewSegments (Stable)
* The number of distinct segments to stop at when sweeping from left to right using the observation angle
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
### Constraints
* MinRelativeSpeed must be less or equal than MaxRelativeSpeed
* MinWalkDistance must be less or equal than MaxWalkDistance
* MinNodeDelay must be less or equal than MaxNodeDelay
* Must be attached to a sensor that provides one of path
[(Top)](#top)
## Random: Instruction (Stable)
Randomised list of weighted instructions. One will be selected at random and executed until the NPC state changes.
### Attributes
#### - Name (Stable)
* Optional name for descriptor
* **Type**: `String`, **Optional** (Default: null)
#### - Tag (Experimental)
* Internal identifier tag for debugging
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - Enabled (Stable)
* Whether this instruction should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor for testing if instruction can be applied. If not supplied, will always match
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Sensor
#### - Instructions (Stable)
* List of weighted instructions to select from
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Instruction
#### - Continue (WorkInProgress)
* Continue after this instruction was executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Weight (Stable)
* Weighted chance of picking this instruction in a random instruction
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - TreeMode (Stable)
* Whether this instruction and its contents should be treated like a traditional behaviour tree, i.e. will continue if all child instructions fail
* **Type**: `Boolean`, **Optional** (Default: false)
#### - InvertTreeModeResult (Stable)
* Whether or not to invert the result of TreeMode evaluation when passing up to parent TreeMode instructions
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - ResetOnStateChange (Stable)
* Whether to reset when NPC state changes
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ExecuteFor (Stable)
* How long to execute the chosen instruction before picking another
* **Type**: `Array`, **Optional** (Default: \[1.7976931348623157E308, 1.7976931348623157E308]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
### Constraints
* If TreeMode is true, Continue must be false
[(Top)](#top)
## Reference: Instruction (Stable)
Prioritized instruction list that can be referenced from elsewhere in the file. Otherwise works like the default
### Attributes
#### - Parameters (Stable)
* The parameter block for defining variables
* **Type**: `Parameters`, **Optional** (Default: )
#### - Name (Stable)
* Name for referencing
* **Type**: `String`, **Required**
#### - Tag (Experimental)
* Internal identifier tag for debugging
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - Enabled (Stable)
* Whether this instruction should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor for testing if instruction can be applied. If not supplied, will always match
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Sensor
#### - BodyMotion (Stable)
* Body motion to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: BodyMotion
#### - HeadMotion (Stable)
* Head motion to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: HeadMotion
#### - Actions (Stable)
* Actions to be executed
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ActionList
#### - ActionsBlocking (Stable)
* Do not execute an action unless the previous action could execute
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ActionsAtomic (Stable)
* Only execute actions if all actions can be executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Instructions (Stable)
* Optional nested list of instructions
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: Instruction
#### - Continue (WorkInProgress)
* Continue after this instruction was executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Weight (Stable)
* Weighted chance of picking this instruction in a random instruction
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - TreeMode (Stable)
* Whether this instruction and its contents should be treated like a traditional behaviour tree, i.e. will continue if all child instructions fail
* **Type**: `Boolean`, **Optional** (Default: false)
#### - InvertTreeModeResult (Stable)
* Whether or not to invert the result of TreeMode evaluation when passing up to parent TreeMode instructions
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Constraints
* At most one or none of BodyMotion, Instructions must be provided
* At most one or none of HeadMotion, Instructions must be provided
* At most one or none of Actions, Instructions must be provided
* If TreeMode is true, Continue must be false
[(Top)](#top)
## RelativeWaypointDefinition: RelativeWaypointDefinition (Stable)
A simple path waypoint definition where each waypoint is relative to the previous
### Attributes
#### - Rotation (Stable)
* Rotation to turn from previous waypoint
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater than -360 and less than 360
#### - Distance (Stable)
* A distance to move from the previous waypoint
* **Type**: `Double`, **Required**
* **Constraint**: Value must be greater than 0
[(Top)](#top)
## Seek: BodyMotion (Experimental)
Move towards a target using path finding or steering
### Attributes
#### - RelativeSpeed (Stable)
* Maximum relative speed the NPC should move
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - RelativeSpeedWaypoint (Stable)
* Maximum relative speed the NPC should move close to waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1
#### - WaypointRadius (Stable)
* Radius to slow down around waypoints
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0.1
#### - UseBestPath (Stable)
* Use best partial path if goal can't be reached
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ThrottleDelayRange (Stable)
* Time to delay after no path finding solution found
* **Type**: `Array`, **Optional** (Default: \[3.0, 5.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - ThrottleIgnoreCount (Stable)
* How often no valid path solution can be found before throttling delay is applied
* **Type**: `Integer`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - BuildOptimisedPath (Stable)
* Try to reduce number of nodes of generated path
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlendHeading (Stable)
* Relative rotation angle into next waypoint when arriving at current waypoint
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 1
#### - PathSmoothing (Stable)
* Try to smooth followed path. Larger values smooth more.
* **Type**: `Integer`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - RejectionWeight (Stable)
* Weight of rejection vector pushing entity closer to original path
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - UseSteering (Stable)
* Use simple/cheap steering if available
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - SkipSteering (Experimental)
* Skip steering if target not reachable
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - UsePathfinder (Stable)
* Use path finder
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MinPathLength (Experimental)
* Minimum length of path required when not able to reach target (should be greater equal 2)
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - DiagonalMoves (Stable)
* Allow diagonal moves
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - StepsPerTick (Stable)
* Steps per iteration
* **Type**: `Integer`, **Optional** (Default: 50.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxPathLength (Stable)
* Max path steps before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxOpenNodes (Stable)
* Max open nodes before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 200.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MaxTotalNodes (Stable)
* Max total node before aborting path finding
* **Type**: `Integer`, **Optional** (Default: 900.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Debug (Stable)
* Debugging flags
* **Type**: `String`, **Optional** (Default: )
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - WaitDistance (Experimental)
* Minimum distance target needs to move before recomputing path when no path can be found
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - RecomputeDistance (Experimental)
* Maximum distance target can move before path is recomputed or 0 to supress recomputation
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - ReprojectDistance (Experimental)
* Maximum distance target can move before position is reprojected
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - AdjustRangeByHitboxSize (Stable)
* Correct range by hitbox sizes of involved entities
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - RecomputeConeAngle (Experimental)
* Recompute path when target leaves cone from initial position to target
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - Reachable (Experimental)
* Target must be reachable so that hitboxes can overlap
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - HeightDifference (Experimental)
* Height difference allowed to target
* **Type**: `Array`, **Optional** (Default: \[-1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than -1.7976931348623157e+308, less or equal than 1.7976931348623157e+308and in strictly ascending order
#### - SlowDownDistance (Stable)
* Distance when to slow down when approaching
* **Type**: `Double`, **Optional** (Default: 8.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance to stop at
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AbortDistance (Stable)
* Distance to abort behaviour
* **Type**: `Double`, **Optional** (Default: 96.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Falloff (Stable)
* Deceleration when approaching target
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - SwitchToSteeringDistance (Stable)
* Distance below NPC can test if target is reachable and abort existing path
* **Type**: `Double`, **Optional** (Default: 20.0), **Computable**
* **Constraint**: Value must be greater than 0
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
* SlowDownDistance must be greater or equal than StopDistance
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## Sequence: BodyMotion (Stable)
Sequence of motions. Can be used in conjunction with 'Timer' to model more complex motions.
### Attributes
#### - Looped (Stable)
* When true restart after last motion is finished
* **Type**: `Boolean`, **Optional** (Default: true)
#### - RestartOnActivate (Experimental)
* Restart from first motion when NPC is activated.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Motions (Stable)
* Array of motions
* **Type**: `Array`, **Required**, **Element Type**: BodyMotion
* **Constraint**: Array must not be empty
### Constraints
[(Top)](#top)
## Sequence: HeadMotion (Stable)
Sequence of motions. Can be used in conjunction with 'Timer' to model more complex motions.
### Attributes
#### - Looped (Stable)
* When true restart after last motion is finished
* **Type**: `Boolean`, **Optional** (Default: true)
#### - RestartOnActivate (Experimental)
* Restart from first motion when NPC is activated.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Motions (Stable)
* Array of motions
* **Type**: `Array`, **Required**, **Element Type**: HeadMotion
* **Constraint**: Array must not be empty
### Constraints
[(Top)](#top)
## SpotsMe: IEntityFilter (Stable)
Checks if the entity can view the NPC in a given view sector or cone and without obstruction.
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ViewAngle (Stable)
* Angle used for view sector and view cone test
* **Type**: `Double`, **Optional** (Default: 90.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - ViewTest (Stable)
* Check if the entity is in the view cone, view sector, or neither
* **Type**: `Flag`, **Optional** (Default: VIEW\_SECTOR)
* **Flag Values**:
* `VIEW_SECTOR`: View\_Sector
* `NONE`: None
* `VIEW_CONE`: View\_Cone
#### - TestLineOfSight (Stable)
* Check if view to the npc is not obstructed
* **Type**: `Boolean`, **Optional** (Default: true)
### Constraints
[(Top)](#top)
## StandingOnBlock: IEntityFilter (Stable)
Matches the block directly beneath the entity against a BlockSet
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - BlockSet (Stable)
* The BlockSet to match against
* **Type**: `Asset`, **Required**, **Computable**
[(Top)](#top)
## Stat: IEntityFilter (Stable)
Match stat values of the entity
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Stat (Stable)
* The stat value to check
* **Type**: `Asset`, **Required**, **Computable**
#### - StatTarget (Stable)
* The stat target
* **Type**: `Flag`, **Required**, **Computable**
* **Flag Values**:
* `Min`: Min value
* `Max`: Max value
* `Value`: Current value
#### - RelativeTo (Stable)
* The stat value to check against
* **Type**: `Asset`, **Required**, **Computable**
#### - RelativeToTarget (Stable)
* The stat target
* **Type**: `Flag`, **Required**, **Computable**
* **Flag Values**:
* `Min`: Min value
* `Max`: Max value
* `Value`: Current value
#### - ValueRange (Stable)
* The fractional range within which to trigger, with 1 being equal
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
[(Top)](#top)
## StateTransition: StateTransition (Stable)
An entry containing a list of actions to execute when moving from one state to another
### Attributes
#### - States (Stable)
* List of state transitions
* **Type**: `Array`, **Required**, **Element Type**: StateTransitionEdges
#### - Actions (Stable)
* List of actions
* **Type**: `ObjectRef`, **Required**, **Object Type**: ActionList
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## StateTransitionController: StateTransitionController (Stable)
A list of state transitions
* A list of state transition entries with lists of actions
* **Type**: `Array`, **Required**, **Element Type**: StateTransition
[(Top)](#top)
## StateTransitionEdges: StateTransitionEdges (Stable)
Sets of from and to states defining state transitions
### Attributes
#### - Priority (Stable)
* Priority for the actions in this transition
* **Type**: `Integer`, **Optional** (Default: 0)
* **Constraint**: Value must be greater or equal than 0
#### - From (Stable)
* A set of from states
* **Type**: `StringList`, **Required**
* **Constraint**: Strings in array must not be empty
#### - To (Stable)
* A set of to states
* **Type**: `StringList`, **Required**
* **Constraint**: Strings in array must not be empty
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## TakeOff: BodyMotion (Experimental)
Switch NPC from walking to flying motion controller
### Attributes
#### - JumpSpeed (Experimental)
* Speed to jump off
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0
### Constraints
[(Top)](#top)
## Teleport: BodyMotion (Experimental)
Teleport NPC to a position given by a sensor or to a random position nearby with an optional minimum offset up to a maximum offset
### Attributes
#### - OffsetRange (Experimental)
* The minimum and maximum offset the NPC can be spawned from the target position
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - MaxYOffset (Experimental)
* Maximum vertical offset from the target position in case of terrain obstacles
* **Type**: `Double`, **Optional** (Default: 5.0)
* **Constraint**: Value must be greater than 0
#### - OffsetSector (Experimental)
* The sector around the target in which to teleport to. The origin point is directly between the target and the NPC teleporting
* **Type**: `Double`, **Optional** (Default: 0.0)
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - Orientation (Experimental)
* The direction to face after teleporting
* **Type**: `Flag`, **Optional** (Default: Unchanged)
* **Flag Values**:
* `UseTarget`: Use the target's orientation
* `TowardsTarget`: Face towards the target
* `Unchanged`: Do not change orientation
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
* If Orientation is UseTarget, must be attached to a sensor that provides one of player target, NPC target, dropped item target
[(Top)](#top)
## TestProbe: BodyMotion (Experimental)
Debugging - Test probing
### Attributes
#### - AdjustX (Experimental)
* X block position adjustment
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - AdjustZ (Experimental)
* Y block position adjustment
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - AdjustDistance (Experimental)
* Set probe direction length for debugging
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - SnapAngle (Experimental)
* Snap angle to multiples of value for debugging
* **Type**: `Double`, **Optional** (Default: -1.0)
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true)
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
[(Top)](#top)
## Timer: BodyMotion (Stable)
Execute a Motion for a specific maximum time. If the motion finishes earlier the Timer also finishes.
### Attributes
#### - Time (Stable)
* Range of time from which the random timer length can be chosen
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Motion (Stable)
* Motion to execute
* **Type**: `ObjectRef`, **Required**, **Object Type**: BodyMotion
### Constraints
[(Top)](#top)
## Timer: HeadMotion (Stable)
Execute a Motion for a specific maximum time. If the motion finishes earlier the Timer also finishes.
### Attributes
#### - Time (Stable)
* Range of time from which the random timer length can be chosen
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Motion (Stable)
* Motion to execute
* **Type**: `ObjectRef`, **Required**, **Object Type**: HeadMotion
### Constraints
[(Top)](#top)
## ValueToParameterMapping: ValueToParameterMapping (Stable)
An entry containing a list of actions to execute when moving from one state to another
### Attributes
#### - ValueType (Stable)
* The type of the value being mapped
* **Type**: `Flag`, **Required**
* **Flag Values**:
* `String`: String value
* `Double`: Double value
* `Int`: Integer value
#### - FromValue (Stable)
* The value to read from the value store
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
#### - ToParameter (Stable)
* The parameter name to override
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
[(Top)](#top)
## ViewSector: IEntityFilter (Stable)
Matches entities within the given view sector
### Attributes
#### - Enabled (Stable)
* Whether this entity filter should be enabled
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ViewSector (Stable)
* View sector to test entities in
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
### Constraints
[(Top)](#top)
## Wander: BodyMotion (Stable)
Random movement in short linear pieces.
### Attributes
#### - MinWalkTime (Stable)
* Minimum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxWalkTime (Stable)
* Maximum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 4.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinHeadingChange (Stable)
* Approximate minimum heading change between segments
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - MaxHeadingChange (Stable)
* Approximate maximum heading change between segments
* **Type**: `Double`, **Optional** (Default: 90.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - RelaxHeadingChange (Stable)
* Allow other directions when preferred directions blocked
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelativeSpeed (Stable)
* Relative wander speed
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - MinMoveDistance (Stable)
* Minimum distance to move in a segment
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance to stop at target
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - TestsPerTick (Stable)
* Direction tests per tick
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
### Constraints
* MinWalkTime must be less or equal than MaxWalkTime
* MinHeadingChange must be less or equal than MaxHeadingChange
[(Top)](#top)
## WanderInCircle: BodyMotion (Stable)
Random movement in short linear pieces inside circle around spawn position.
### Attributes
#### - MinWalkTime (Stable)
* Minimum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxWalkTime (Stable)
* Maximum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 4.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinHeadingChange (Stable)
* Approximate minimum heading change between segments
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - MaxHeadingChange (Stable)
* Approximate maximum heading change between segments
* **Type**: `Double`, **Optional** (Default: 90.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - RelaxHeadingChange (Stable)
* Allow other directions when preferred directions blocked
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelativeSpeed (Stable)
* Relative wander speed
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - MinMoveDistance (Stable)
* Minimum distance to move in a segment
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance to stop at target
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - TestsPerTick (Stable)
* Direction tests per tick
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - Radius (Stable)
* Radius of circle to wander in
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Flock (Experimental)
* Do not use
* **Type**: `Boolean`, **Optional** (Default: false)
#### - UseSphere (Stable)
* Use a sphere instead of circle cylinder
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
* MinWalkTime must be less or equal than MaxWalkTime
* MinHeadingChange must be less or equal than MaxHeadingChange
[(Top)](#top)
## WanderInRect: BodyMotion (Stable)
Random movement in short linear pieces inside rectangle around spawn position.
### Attributes
#### - MinWalkTime (Stable)
* Minimum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 2.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxWalkTime (Stable)
* Maximum time to wander for a segment.
* **Type**: `Double`, **Optional** (Default: 4.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - MinHeadingChange (Stable)
* Approximate minimum heading change between segments
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - MaxHeadingChange (Stable)
* Approximate maximum heading change between segments
* **Type**: `Double`, **Optional** (Default: 90.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 180
#### - RelaxHeadingChange (Stable)
* Allow other directions when preferred directions blocked
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelativeSpeed (Stable)
* Relative wander speed
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
#### - MinMoveDistance (Stable)
* Minimum distance to move in a segment
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - StopDistance (Stable)
* Distance to stop at target
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
#### - TestsPerTick (Stable)
* Direction tests per tick
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AvoidBlockDamage (Stable)
* Should avoid environmental damage from blocks
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - RelaxedMoveConstraints (Stable)
* NPC can do movements like wading (depends on motion controller type)
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - DesiredAltitudeWeight (Stable)
* How much this NPC prefers being within the desired height range. 0 means it doesn't care much, 1 means it will do its best to get there fast. Values below 0 mean the default in the motion controller will be used.
* **Type**: `Double`, **Optional** (Default: -1.0), **Computable**
* **Constraint**: Value must be greater or equal than -1 and less or equal than 1
#### - Width (Stable)
* Rectangle width
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
#### - Depth (Stable)
* Rectangle depth
* **Type**: `Double`, **Optional** (Default: 10.0)
* **Constraint**: Value must be greater than 0
### Constraints
* MinWalkTime must be less or equal than MaxWalkTime
* MinHeadingChange must be less or equal than MaxHeadingChange
[(Top)](#top)
## Watch: HeadMotion (Stable)
Rotate to target.
### Attributes
#### - RelativeTurnSpeed (Stable)
* The relative turn speed modifier
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 2
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
## WeightedAction: WeightedAction (Stable)
A wrapped and weighted action intended to be used for Random action lists.
### Attributes
#### - Action (Stable)
* The action to run
* **Type**: `ObjectRef`, **Required**, **Object Type**: Action
#### - Weight (Stable)
* The weight representing how likely this action is to run
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
[(Top)](#top)
# List of Core Components (audiovisual)
## Animation: Sensor (Stable)
Check if a given animation is being played
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Slot (Stable)
* The animation slot to check
* **Type**: `Flag`, **Required**, **Computable**
* **Flag Values**:
* `Status`: Status
* `Action`: Action
* `Face`: Face
#### - Animation (Stable)
* The animation ID to check for
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## Appearance: Action (Stable)
Change model of NPC to given appearance.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Appearance (Stable)
* Model name to use
* **Type**: `Asset`, **Required**
[(Top)](#top)
## DisplayName: Action (Stable)
Set the name displayed above NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - DisplayName (Stable)
* Name to display above NPC
* **Type**: `String`, **Required**, **Computable**
[(Top)](#top)
## ModelAttachment: Action (Stable)
Set an attachment on the current NPC model
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Slot (Stable)
* The attachment slot to set
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Attachment (Stable)
* The attachment to set, or empty to remove
* **Type**: `String`, **Required**, **Computable**
[(Top)](#top)
## PlayAnimation: Action (Experimental)
Play an animation
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Slot (Stable)
* The animation slot to play on
* **Type**: `Flag`, **Required**
* **Flag Values**:
* `Status`: Status
* `Action`: Action
* `Face`: Face
#### - Animation (Stable)
* The animation ID to play
* **Type**: `String`, **Optional** (Default: null), **Computable**
[(Top)](#top)
## PlaySound: Action (Stable)
Plays a sound to players within a specified range.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - SoundEventId (Stable)
* The sound event to play
* **Type**: `Asset`, **Required**, **Computable**
[(Top)](#top)
## SpawnParticles: Action (WorkInProgress)
Spawn particle system visible within a given range with an offset relative to npc heading
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ParticleSystem (Stable)
* Particle system to spawn
* **Type**: `Asset`, **Required**, **Computable**
#### - Range (Stable)
* Maximum visibility range
* **Type**: `Double`, **Optional** (Default: 75.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Offset (Stable)
* Offset relative to footpoint in view direction of NPC
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: Double
[(Top)](#top)
# List of Core Components (combat)
## ApplyEntityEffect: Action (Stable)
Applies an entity effect to the target or self
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - EntityEffect (Stable)
* The entity effect to apply
* **Type**: `Asset`, **Required**, **Computable**
#### - UseTarget (Stable)
* Use the sensor-provided target for the action, self otherwise
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
* If UseTarget is true, must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## Attack: Action (Experimental)
Let NPC start an attack. When an attack is running no new attack is started.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Attack (Experimental)
* Attack pattern to use. If omitted, will cancel current attack
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - AttackType (Stable)
* The interaction type to use
* **Type**: `Flag`, **Optional** (Default: Primary), **Computable**
* **Flag Values**:
* `Secondary`: Secondary attack
* `Ability3`: Ability 3
* `Primary`: Primary attack
* `Ability2`: Ability 2
* `Ability1`: Ability 1
#### - ChargeFor (Stable)
* How long to charge for. 0 indicates no charging. Also doubles as how long to block for
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - AttackPauseRange (Stable)
* Range of minimum pause between attacks
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - AimingTimeRange (Stable)
* A range from which to pick a random value denoting the max time the NPC will wait for aiming before launching the attack.
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - LineOfSight (Experimental)
* Check Line of Sight before firing
* **Type**: `Boolean`, **Optional** (Default: false)
#### - AvoidFriendlyFire (Experimental)
* Tries to avoid friendly fire if true
* **Type**: `Boolean`, **Optional** (Default: true)
#### - BallisticMode (WorkInProgress)
* Trajectory to use
* **Type**: `Flag`, **Optional** (Default: Short)
* **Flag Values**:
* `Random`: Random long or short
* `Long`: Longer flight curve
* `Short`: Shorter flight curve
* `Alternate`: Alternate between long and short
#### - MeleeConeAngle (WorkInProgress)
* Cone angle considered for on target for melee
* **Type**: `Double`, **Optional** (Default: 30.0)
* **Constraint**: Value must be greater than 0 and less or equal than 360
#### - DamageFriendlies (Stable)
* Whether this attack should bypass ignored damage groups and deal damage to the target
* **Type**: `Boolean`, **Optional** (Default: false)
#### - SkipAiming (Stable)
* Whether aiming should be skipped an the attack just executed immediately.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ChargeDistance (Stable)
* If this is a charge attack, the distance required for the charge
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - InteractionVars (Stable)
* Set of interaction vars for modifying the interaction
* **Type**: `CodecObject`, **Optional** (Default: null)
### Constraints
* If SkipAiming is true, LineOfSight, AvoidFriendlyFire must be false
[(Top)](#top)
## Damage: Sensor (Stable)
Test if NPC suffered damage. A position is only returned when NPC suffered combat damage.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Combat (Stable)
* Test for combat damage
* **Type**: `Boolean`, **Optional** (Default: true)
#### - Friendly (Stable)
* Test for damage from usually disabled damage groups
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Drowning (Stable)
* Test for damage from drowning
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Environment (Stable)
* Test for damage from environment
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Other (Stable)
* Test for other damage
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The slot to use for locking on the target if damage is taken. If omitted, target will not be locked
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
### Constraints
* At least one of Combat, Drowning, Environment, Other must be true
* If TargetSlot is true, Drowning, Environment, Other must be false
### Provides
* Player target
* NPC target
* Dropped item target
[(Top)](#top)
## IsBackingAway: Sensor (Stable)
Test if the NPC is currently backing away from something.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
# List of Core Components (corecomponents)
## AddToHostileTargetMemory: Action (Stable)
Adds the passed target from the sensor to the hostile target memory
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## CombatAbility: Action (Stable)
Starts the combat ability selected by the combat action evaluator.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## CombatActionEvaluator: Sensor (Experimental)
A sensor which handles funnelling information to actions and motions from the combat action evaluator. Delivers the current attack target and desired range for supported direct child motions.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - TargetInRange (Stable)
* Whether to match on target being in or out of range.
* **Type**: `Boolean`, **Required**, **Computable**
#### - AllowableDeviation (Stable)
* The allowable deviation from the desired attack range
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
### Provides
* Player target
* NPC target
[(Top)](#top)
## FlockBeacon: Action (Experimental)
Let the NPC send out a message to the flock members
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Message (Stable)
* Message to send to targets
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - SendTargetSlot (Stable)
* The marked target slot to send. If omitted, sends own position
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - ExpirationTime (Stable)
* The number of seconds that the message should last and be acknowledged by the receiving NPC. -1 represents infinite time.
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0 or equal -1
#### - SendToSelf (Stable)
* Send the message to self
* **Type**: `Boolean`, **Optional** (Default: true)
#### - SendToLeaderOnly (Stable)
* Only send the message to the leader of the flock
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## FlockCombatDamage: Sensor (Stable)
Return true if flock with NPC received combat damage. Target position is entity which did most damage.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - LeaderOnly (Stable)
* Only test for damage to flock leader
* **Type**: `Boolean`, **Optional** (Default: true)
### Provides
* Player target
* NPC target
[(Top)](#top)
## FlockLeader: Sensor (Stable)
Test for the presence and provide position of the flock leader
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Provides
* Player target
* NPC target
[(Top)](#top)
## FlockState: Action (Stable)
Sets the state name for the flock the NPC is member of.The flock leader is explicitly excluded from this operation.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - State (Stable)
* State name to set
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be a valid state string. A main state must be included before the period (e.g. Main.Test). State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
### Constraints
[(Top)](#top)
## FlockTarget: Action (Stable)
Sets or clears the locked target for the flock the NPC is member of. If Clear flag is true, the locked target is cleared otherwise it is set to the the target.The flock leader is explicitly excluded from this operation.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Clear (Stable)
* If true, clear locked target. If false, set to current target.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The target slot to use
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String must be not empty
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## HasHostileTargetMemory: Sensor (Stable)
Checks if there is currently a hostile target in the target memory.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## InflictedDamage: Sensor (Stable)
Return true if an individual or the flock it belongs to inflicted combat damage. Target position is entity which received most damage.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Target (Stable)
* Who to check has inflicted damage
* **Type**: `Flag`, **Optional** (Default: Self)
* **Flag Values**:
* `FlockLeader`: Check flock leader only
* `Self`: Check self
* `Flock`: Check flock
#### - FriendlyFire (Stable)
* Consider friendly fire too
* **Type**: `Boolean`, **Optional** (Default: false)
### Provides
* Player target
* NPC target
[(Top)](#top)
## JoinFlock: Action (Stable)
Tries to build/join flock with target. Fails if both NPC and target are in a flock. If either NPC or target are in a flock, the one not in flock tries to join existing flock.If NPC and target are both not in a flock, a new flock with NPC is created and target is tried to be joined.Joining the flock can be rejected if the joining entity does have the correct type or the flock is full. This can be overridden by setting the ForceJoin flag to true.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ForceJoin (Stable)
* Disables checking flock join conditions test and forces joining flock.
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## LeaveFlock: Action (Stable)
NPC leaves flock currently in. Does nothing when not in flock.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## TriggerSpawnBeacon: Action (Stable)
Trigger the nearest spawn beacon matching the configuration id
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - BeaconSpawn (Stable)
* The beacon spawn config ID
* **Type**: `Asset`, **Required**, **Computable**
#### - Range (Stable)
* The distance to search for a beacon to trigger
* **Type**: `Integer`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - TargetSlot (Stable)
* A slot to use as the target for the spawned NPC. If omitted the NPC itself will be used
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
[(Top)](#top)
# List of Core Components (debug)
## Log: Action (Stable)
Log a message to console.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Message (Stable)
* Text to print to console.
* **Type**: `String`, **Required**, **Computable**
[(Top)](#top)
## Test: Action (Experimental)
Test action to exercise attribute evaluation (DO NOT USE)
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Boolean (Deprecated)
* Boolean True
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Double (Deprecated)
* Double 0
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
#### - Float (Deprecated)
* Float 0
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
#### - Int (Deprecated)
* Int 0
* **Type**: `Integer`, **Optional** (Default: 0.0), **Computable**
#### - String (Deprecated)
* String Test
* **Type**: `String`, **Optional** (Default: Test), **Computable**
#### - Enum (Deprecated)
* Enum RoleDebugFlags Collisions
* **Type**: `Flag`, **Optional** (Default: Collisions), **Computable**
* **Flag Values**:
* `Overlaps`: Log overlapping blocks when validating position
* `ProbeBlockCollisions`: Trace collisions down to block level when probing
* `SteeringRole`: Debug blended steering behaviour from role like avoidance/separation
* `DisplayInternalId`: Display the internal server ID for this entity
* `Pathfinder`: Display pathfinder status
* `DisplayStamina`: Display NPC Stamina as numerical values
* `FlockDamage`: Trace flock damage events
* `Collisions`: Trace collision information of motion controllers
* `DisplayFreeSlots`: Display free inventory slots
* `DisplayTime`: Set display name to day time
* `DisplayName`: Display the role name for this entity
* `DisplayLightLevel`: Display light levels
* `DisplayAnim`: Display animation state
* `TraceSensorFailures`: Trace failing sensors
* `DisplayHP`: Display NPC HP as numerical values
* `DisplayFlock`: Set display name to flock state
* `DisplaySpeed`: Display speed of entity
* `ValidateMath`: Validate (some) math computations in movement
* `VisSeparation`: Visualize separation vector
* `DisplayState`: Set display name to contents of state
* `TraceSuccess`: Trace matched instructions
* `MotionControllerMove`: Trace movement activity of motion controllers
* `TraceFail`: Trace failed instructions
* `DisplayCustom`: Display custom debug information (generated by debug components)
* `VisAvoidance`: Visualize avoidance vectors
* `Flock`: Trace flock events
* `MotionControllerSteer`: Trace steering activity of motion controllers
* `BlockCollisions`: Trace collisions down to block level
* `ValidatePositions`: Validate computed movement positions are not intersecting blocks
* `DisplayTarget`: Set display name to locked target type
* `BeaconMessages`: Enable debugging of beacon message sending and receiving
#### - EnumSet (Deprecated)
* EnumSet Collisions Flock
* **Type**: `FlagSet`, **Optional** (Default: \[Flock, Collisions]), **Computable**
* **Flag Values**:
* `Overlaps`: Log overlapping blocks when validating position
* `ProbeBlockCollisions`: Trace collisions down to block level when probing
* `SteeringRole`: Debug blended steering behaviour from role like avoidance/separation
* `DisplayInternalId`: Display the internal server ID for this entity
* `Pathfinder`: Display pathfinder status
* `DisplayStamina`: Display NPC Stamina as numerical values
* `FlockDamage`: Trace flock damage events
* `Collisions`: Trace collision information of motion controllers
* `DisplayFreeSlots`: Display free inventory slots
* `DisplayTime`: Set display name to day time
* `DisplayName`: Display the role name for this entity
* `DisplayLightLevel`: Display light levels
* `DisplayAnim`: Display animation state
* `TraceSensorFailures`: Trace failing sensors
* `DisplayHP`: Display NPC HP as numerical values
* `DisplayFlock`: Set display name to flock state
* `DisplaySpeed`: Display speed of entity
* `ValidateMath`: Validate (some) math computations in movement
* `VisSeparation`: Visualize separation vector
* `DisplayState`: Set display name to contents of state
* `TraceSuccess`: Trace matched instructions
* `MotionControllerMove`: Trace movement activity of motion controllers
* `TraceFail`: Trace failed instructions
* `DisplayCustom`: Display custom debug information (generated by debug components)
* `VisAvoidance`: Visualize avoidance vectors
* `Flock`: Trace flock events
* `MotionControllerSteer`: Trace steering activity of motion controllers
* `BlockCollisions`: Trace collisions down to block level
* `ValidatePositions`: Validate computed movement positions are not intersecting blocks
* `DisplayTarget`: Set display name to locked target type
* `BeaconMessages`: Enable debugging of beacon message sending and receiving
#### - Asset (Deprecated)
* Asset Sheep
* **Type**: `Asset`, **Optional** (Default: Sheep), **Computable**
#### - DoubleArray (Deprecated)
* DoubleArray \[1,2] 0-10
* **Type**: `Array`, **Optional** (Default: \[1.0, 2.0]), **Computable**, **Element Type**: Double
#### - StringArray (Deprecated)
* StringArray \[a,b] 0-10
* **Type**: `Array`, **Optional** (Default: \[a, b]), **Computable**, **Element Type**: String
[(Top)](#top)
# List of Core Components (entity)
## Beacon: Sensor (Stable)
Checks to see if any messages have been broadcasted by nearby NPCs.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Message (Experimental)
* The message to listen for
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Range (Experimental)
* The max distance beacons should be received from
* **Type**: `Double`, **Optional** (Default: 64.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - TargetSlot (Stable)
* A slot to store the sender as a target. If omitted no target will be stored
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
#### - ConsumeMessage (Stable)
* Whether the message should be consumed by this sensor
* **Type**: `Boolean`, **Optional** (Default: true)
### Provides
* Player target
* NPC target
* Dropped item target
[(Top)](#top)
## Count: Sensor (Stable)
Check if there is a certain number of NPCs or players within a specific range
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Count (Stable)
* Specifies the allowed number of entities (inclusive)
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: integer
* **Constraint**: Values must be greater or equal than 0, less or equal than 2147483647and in weakly ascending order
#### - Range (Stable)
* Range to find entities in (inclusive)
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - IncludeGroups (Stable)
* Match for NPCs in these groups
* **Type**: `AssetArray`, **Optional** (Default: null), **Element Type**: TagSet
#### - ExcludeGroups (Stable)
* Never match NPCs in these groups
* **Type**: `AssetArray`, **Optional** (Default: null), **Element Type**: TagSet
[(Top)](#top)
## IgnoreForAvoidance: Action (Stable)
Set the target slot of an entity that should be ignored during avoidance
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The target slot containing the entity to be ignored
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## Kill: Sensor (Stable)
Return true if NPC made a kill. Target position is killed entity position.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - TargetSlot (Stable)
* The target slot to check if killed. If omitted, will accept any entity killed
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
### Provides
* Vector position
[(Top)](#top)
## Mob: Sensor (Stable)
Return true if entity matching specific attributes and filters is in range. Target is entity.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MinRange (Stable)
* Minimum range to test entities in
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - Range (Stable)
* Maximum range to test entities in
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - LockOnTarget (Stable)
* Matched target becomes locked target
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - LockedTargetSlot (Stable)
* The target slot to use for locking on or unlocking
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String value must be either null or not empty
#### - AutoUnlockTarget (Stable)
* Unlock locked target when sensor not matching it anymore
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - OnlyLockedTarget (Stable)
* Test only locked target
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - IgnoredTargetSlot (Stable)
* The target slot to use for ignoring
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - UseProjectedDistance (Stable)
* Use the projected movement direction vector for distance, rather than the Euclidean distance
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - Prioritiser (Stable)
* A prioritiser for selecting results based on additional parameters
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ISensorEntityPrioritiser
#### - Collector (Stable)
* A collector which can process all checked entities and act on them based on whether they match or not
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ISensorEntityCollector
#### - Filters (Stable)
* A series of entity filter sensors to test
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: IEntityFilter
#### - GetPlayers (Stable)
* Test players
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - GetNPCs (Stable)
* Test mobs/NPCs
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ExcludeOwnType (Stable)
* Exclude NPCs of same type as current NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
* Range must be greater or equal than MinRange
* At least one of GetPlayers, GetNPCs must be true
### Provides
* Player target
* NPC target
[(Top)](#top)
## OverrideAttitude: Action (Stable)
Override this NPCs attitude towards the provided target for a given duration
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Attitude (Stable)
* The attitude to set
* **Type**: `Flag`, **Required**, **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
#### - Duration (Stable)
* The duration to override for
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## Player: Sensor (Stable)
Return true if player matching specific attributes and filters is in range. Target is player.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MinRange (Stable)
* Minimum range to test entities in
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - Range (Stable)
* Maximum range to test entities in
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - LockOnTarget (Stable)
* Matched target becomes locked target
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - LockedTargetSlot (Stable)
* The target slot to use for locking on or unlocking
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String value must be either null or not empty
#### - AutoUnlockTarget (Stable)
* Unlock locked target when sensor not matching it anymore
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - OnlyLockedTarget (Stable)
* Test only locked target
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - IgnoredTargetSlot (Stable)
* The target slot to use for ignoring
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - UseProjectedDistance (Stable)
* Use the projected movement direction vector for distance, rather than the Euclidean distance
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - Prioritiser (Stable)
* A prioritiser for selecting results based on additional parameters
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ISensorEntityPrioritiser
#### - Collector (Stable)
* A collector which can process all checked entities and act on them based on whether they match or not
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: ISensorEntityCollector
#### - Filters (Stable)
* A series of entity filter sensors to test
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: IEntityFilter
### Constraints
* Range must be greater or equal than MinRange
### Provides
* Player target
* NPC target
[(Top)](#top)
## ReleaseTarget: Action (Stable)
Clear locked target for NPC.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The target slot to release
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## Self: Sensor (Stable)
Test if the NPC itself matches a set of entity filters
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Filters (Stable)
* A series of entity filter sensors to test
* **Type**: `Array`, **Required**, **Element Type**: IEntityFilter
### Provides
* Vector position
[(Top)](#top)
## SetMarkedTarget: Action (Stable)
Explicitly sets a marked target in a given slot.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The target slot to set a target to.
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String must be not empty
### Constraints
* Must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## SetStat: Action (Stable)
Sets (or adds to) an entity stat on the NPC.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Stat (Stable)
* The entity stat to affect.
* **Type**: `Asset`, **Required**, **Computable**
#### - Value (Stable)
* The value to set the stat to.
* **Type**: `Double`, **Required**, **Computable**
#### - Add (Stable)
* Add the value to the existing value instead of setting it.
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
[(Top)](#top)
## Target: Sensor (Stable)
Test if given target matches a series of criteria and optional entity filters
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - TargetSlot (Stable)
* The target slot to check
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String must be not empty
#### - Range (Stable)
* Maximum range of locked target
* **Type**: `Double`, **Optional** (Default: 1.7976931348623157E308), **Computable**
* **Constraint**: Value must be greater than 0
#### - AutoUnlockTarget (Stable)
* Unlock locked target if match fails
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - Filters (Stable)
* A series of entity filter sensors to test
* **Type**: `Array`, **Optional** (Default: null), **Element Type**: IEntityFilter
### Provides
* Player target
* NPC target
[(Top)](#top)
# List of Core Components (interaction)
## CanInteract: Sensor (Stable)
Checks whether or not the player being iterated by the interaction instruction can interact with this NPC
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - ViewSector (Stable)
* View sector to test the player in
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - Attitudes (Stable)
* A set of attitudes to match
* **Type**: `FlagSet`, **Optional** (Default: \[NEUTRAL, FRIENDLY, REVERED]), **Computable**
* **Flag Values**:
* `HOSTILE`: is hostile towards the target
* `REVERED`: reveres the target
* `FRIENDLY`: is friendly towards the target
* `IGNORE`: is ignoring the target
* `NEUTRAL`: is neutral towards the target
### Constraints
[(Top)](#top)
## HasInteracted: Sensor (Stable)
Checks whether the currently iterated player in the interaction instruction has interacted with this NPC
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
[(Top)](#top)
## InteractionContext: Sensor (Stable)
Checks whether the currently iterated player in the interaction instruction has interacted with this NPC in the given context
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Context (Stable)
* The context of the interaction
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
### Constraints
[(Top)](#top)
## LockOnInteractionTarget: Action (Stable)
Locks on to the currently iterated player in the interaction instruction
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - TargetSlot (Stable)
* The target slot to use
* **Type**: `String`, **Optional** (Default: LockedTarget), **Computable**
* **Constraint**: String must be not empty
### Constraints
[(Top)](#top)
## SetInteractable: Action (Stable)
Set whether the currently iterated player in the interaction instruction should be able to interact with this NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Interactable (Stable)
* Toggle whether the currently iterated player in the interaction instruction should be able to interact with this NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Hint (Stable)
* The interaction hint translation key to show for this player (e.g. 'interactionHints.trade')
* **Type**: `String`, **Optional** (Default: null)
#### - ShowPrompt (Stable)
* Whether to show the F-key interaction prompt. Set to false for contextual-only interactions (e.g. shearing with tools). Defaults to true.
* **Type**: `Boolean`, **Optional** (Default: true)
### Constraints
[(Top)](#top)
# List of Core Components (items)
## DropItem: Action (Stable)
Drop an item. Can be a specific item, or from a drop table
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Delay (Stable)
* Range of time to delay in seconds
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Item (Stable)
* A specific item to drop
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - DropList (Stable)
* A reference to an item drop list
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - ThrowSpeed (Stable)
* The throw speed to use
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater than 0 and less or equal than 3.4028234663852886e+38
#### - Distance (Stable)
* The range from which to pick a distance to throw the item
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - DropSector (Stable)
* The sector to spread drops in relative to view direction of NPC in degrees.
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Element Type**: Double
* **Constraint**: Values must be greater or equal than -360, less or equal than 360and in weakly ascending order
#### - PitchHigh (Stable)
* Whether to pitch high or pitch low instead
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
* One (and only one) of Item, DropList must be provided
[(Top)](#top)
## DroppedItem: Sensor (Stable)
Triggers if a given item is within a certain range of the NPC. Will match anything if Items and Attitudes are not defined, otherwise will match items meeting either criteria.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Range (Stable)
* The range within which to look
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - ViewSector (Stable)
* View sector in which to look
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - LineOfSight (Stable)
* Requires line of sight to item
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - Items (Stable)
* A list of glob item patterns to match. If omitted, will match any item
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
#### - Attitudes (Stable)
* Attitudes to match
* **Type**: `FlagSet`, **Optional** (Default: \[]), **Computable**
* **Flag Values**:
* `Neutral`: Neutral
* `Ignore`: Ignore
* `Like`: Like
* `Love`: Love
* `Dislike`: Dislike
### Provides
* Dropped item target
[(Top)](#top)
## Inventory: Action (Stable)
Add or remove a number of items from an inventory. Can also be used to equip them.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Operation (Stable)
* Operation to perform
* **Type**: `Flag`, **Optional** (Default: Add), **Computable**
* **Flag Values**:
* `Add`: Add items to inventory
* `RemoveHeldItem`: Destroy the held item
* `SetHotbar`: Sets the hotbar item in a specific slot
* `Equip`: Equip item as weapon or armour
* `ClearHeldItem`: Clear the held item
* `EquipOffHand`: Equips the item from a specific off-hand slot
* `Remove`: Remove items from inventory
* `EquipHotbar`: Equips the item from a specific hotbar slot
* `SetOffHand`: Sets the off-hand item in a specific slot
#### - Count (Stable)
* Number of items to add/remove
* **Type**: `Integer`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Item (Stable)
* Item type to add, remove, or equip
* **Type**: `Asset`, **Optional** (Default: ), **Computable**
#### - UseTarget (Stable)
* Use the sensor-provided target for the action
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Slot (Stable)
* The hotbar or off-hand to effect. Only valid for Hotbar/OffHand Set/Equip operations
* **Type**: `Integer`, **Optional** (Default: 0.0), **Computable**
### Constraints
* If UseTarget is true, must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## PickUpItem: Action (Stable)
Pick up an item. In hoover mode, will match to the Item array. Otherwise, requires a target to be provided e.g. by a DroppedItemSensor
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Delay (Stable)
* Range of time to delay in seconds
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Range (Stable)
* The range the item will be picked up from
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - StorageTarget (Experimental)
* Where to prioritise putting the item
* **Type**: `Flag`, **Optional** (Default: Hotbar), **Computable**
* **Flag Values**:
* `Destroy`: Destroy the item
* `Hotbar`: Prioritise hotbar
* `Inventory`: Prioritise inventory
#### - Hoover (Stable)
* Suck up all items in range with optional cooldown. Can be filtered with a list of glob patterns. Ignored outside hoover mode
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Items (Stable)
* A list of glob item patterns to match for hoover mode. If omitted, will match any item. Ignored outside hoover mode
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: Item
### Constraints
* If Hoover is false, must be attached to a sensor that provides dropped item target
[(Top)](#top)
# List of Core Components (lifecycle)
## Age: Sensor (Stable)
Triggers when the age of the NPC falls between a certain range. Range is defined in terms of period (e.g. 1Y2M3W4D - 1 year, 2 months, 3 weeks, 4 days) or duration (e.g. 2DT3H4M - 2 days, 3 hours, 4 minutes)
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - AgeRange (Stable)
* The age range within which to trigger
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: TemporalAmount
* **Constraint**: Values must be greater or equal than a few seconds, less or equal than 5879611 years, in strictly ascending order and either all Periods or all Durations
[(Top)](#top)
## DelayDespawn: Action (Stable)
Delay the despawning cycle for some amount of time
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Time (Stable)
* How long to set the to delay
* **Type**: `Double`, **Required**
* **Constraint**: Value must be greater than 0
#### - Shorten (Stable)
* Set the delay to either the current delay or the given time. Whatever is smaller.
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## Despawn: Action (Stable)
Trigger the NPC to start the despawning cycle. If the script contains a despawn sensor it will run that action/motion before removing.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Force (Stable)
* Force the NPC to remove automatically
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## Die: Action (Stable)
Kill the NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## Remove: Action (Stable)
Erase the target entity from the world (no death animation).
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - UseTarget (Stable)
* Use the sensor-provided target for the action
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
* If UseTarget is true, must be attached to a sensor that provides one of player target, NPC target
[(Top)](#top)
## Role: Action (Stable)
Change the Role of the NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Role (Stable)
* The name of the Role to change to
* **Type**: `Asset`, **Required**, **Computable**
#### - ChangeAppearance (Stable)
* Whether the appearance of the new Role should be used
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - State (Stable)
* State name to set
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String must be a valid state string. A main state must be included before the period (e.g. Main.Test). State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
[(Top)](#top)
## Spawn: Action (Experimental)
Spawn an NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - SpawnDirection (Experimental)
* Direction of spawn cone relative to view direction (in degrees)
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than -180 and less or equal than 180
#### - SpawnAngle (Experimental)
* Cone width of spawn direction (in degrees)
* **Type**: `Double`, **Optional** (Default: 360.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - FanOut (Experimental)
* Fan NPCs out equally over angle
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - DistanceRange (Stable)
* Distance from spawner to spawn
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 128and in weakly ascending order
#### - CountRange (Stable)
* Number of NPCs to spawn
* **Type**: `Array`, **Optional** (Default: \[5, 5]), **Computable**, **Element Type**: integer
* **Constraint**: Values must be greater than 0, less or equal than 100and in weakly ascending order
#### - DelayRange (Stable)
* Time between consecutive spawns in seconds
* **Type**: `Array`, **Optional** (Default: \[0.25, 0.25]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Kind (Experimental)
* NPC role to spawn
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Flock (Stable)
* Flock definition to spawn
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - LaunchAtTarget (WorkInProgress)
* Launch the spawned NPC at target position/entity
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - PitchHigh (Stable)
* If launching at a target, use high pitch
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - LaunchSpread (Stable)
* The radius of the circle centred on the target within which to spread thrown NPCs
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - JoinFlock (Stable)
* Whether to join the parent NPC's flock
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - SpawnState (Stable)
* An optional state to set on the spawned NPC if it exists
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - SpawnSubState (Stable)
* An optional substate to set on the spawned NPC if it exists
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
### Constraints
* If LaunchAtTarget is true, must be attached to a sensor that provides one of player target, NPC target, dropped item target, vector position
[(Top)](#top)
# List of Core Components (message)
## Beacon: Action (Experimental)
Let the NPC send out a message to a target group of entities within a certain distance.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Message (Experimental)
* Message to send to targets
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Range (Experimental)
* The maximum range to send the message
* **Type**: `Double`, **Optional** (Default: 64.0)
* **Constraint**: Value must be greater than 0
#### - TargetGroups (Experimental)
* The target group(s) to send the message to
* **Type**: `AssetArray`, **Required**, **Computable**, **Element Type**: TagSet
#### - SendTargetSlot (Stable)
* The target slot of the marked entity to send. Omit to send own position
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - ExpirationTime (Experimental)
* The number of seconds that the message should last and be acknowledged by the receiving NPC. -1 represents infinite time.
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0 or equal -1
#### - SendCount (Experimental)
* Tne number of entities to send the message to. -1 will send to all. Entities will be chosen with a roughly even random distribution using reservoir sampling
* **Type**: `Integer`, **Optional** (Default: -1)
* **Constraint**: Value must be greater than 0 or equal -1
[(Top)](#top)
## Notify: Action (Stable)
Directly notifies a target NPC with a beacon message
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Message (Stable)
* The message to send
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - ExpirationTime (Experimental)
* The number of seconds that the message should last and be acknowledged by the receiving NPC. -1 represents infinite time.
* **Type**: `Double`, **Optional** (Default: 1.0)
* **Constraint**: Value must be greater or equal than 0 or equal -1
#### - UseTargetSlot (Stable)
* A marked target to send to instead of the target provided by a sensor. Omit to use the target provided by the sensor.
* **Type**: `String`, **Optional** (Default: null)
* **Constraint**: String value must be either null or not empty
### Constraints
* If UseTargetSlot is false, must be attached to a sensor that provides NPC target
[(Top)](#top)
# List of Core Components (movement)
## Crouch: Action (Stable)
Set NPC crouching state
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Crouch (Stable)
* True for crouching, false for non-crouching
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## InAir: Sensor (Stable)
Return true if NPC is not on ground. No target is returned.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## MotionController: Sensor (Experimental)
Test if specific motion controller is active.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - MotionController (Experimental)
* Motion controller name to test for
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
[(Top)](#top)
## Nav: Sensor (Stable)
Queries navigation state
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - NavStates (Stable)
* Trigger when path finder is in one of the states or empty to match all
* **Type**: `FlagSet`, **Optional** (Default: \[]), **Computable**
* **Flag Values**:
* `PROGRESSING`: Moving or computing a path
* `INIT`: Doing nothing
* `AT_GOAL`: Reached target
* `BLOCKED`: Can't advance any further
* `ABORTED`: Search stopped but target not reached
* `DEFER`: Delaying/unable to advance
#### - ThrottleDuration (Stable)
* Minimum time in seconds the path finder isn't able to reach target or 0 to ignore
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - TargetDelta (Stable)
* Minimum distance target has moved since path was computed or 0 to ignore
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
[(Top)](#top)
## OnGround: Sensor (Stable)
Return true if NPC is on ground. No target is returned.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## OverrideAltitude: Action (Stable)
Temporarily override the preferred altitude of a flying NPC. Must be refreshed each tick
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - DesiredAltitudeRange (Stable)
* The desired altitude range
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
[(Top)](#top)
## RecomputePath: Action (Stable)
Force recomputation of path finder solution
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
# List of Core Components (npc)
## CompleteTask: Action (Stable)
Complete a task. Tasks are picked based on those provided to SensorCanInteract.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Slot (Stable)
* The animation slot to play on
* **Type**: `Flag`, **Required**
* **Flag Values**:
* `Status`: Status
* `Action`: Action
* `Face`: Face
#### - Animation (Stable)
* The animation ID to play
* **Type**: `String`, **Optional** (Default: null), **Computable**
#### - PlayAnimation (Stable)
* Whether or not to play the animation associated with completing this task
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
### Constraints
[(Top)](#top)
## HasTask: Sensor (Stable)
Checks whether or not the player being iterated by the interaction instruction has any of the given tasks
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - TasksById (Stable)
* Completable tasks to match by name
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: String
* **Constraint**: String array must not be empty
### Constraints
[(Top)](#top)
## Mount: Action (Stable)
Enable the player to mount the entity
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - AnchorX (Stable)
* The X anchor pos
* **Type**: `Double`, **Required**, **Computable**
#### - AnchorY (Stable)
* The Y anchor pos
* **Type**: `Double`, **Required**, **Computable**
#### - AnchorZ (Stable)
* The Z anchor pos
* **Type**: `Double`, **Required**, **Computable**
#### - MovementConfig (Stable)
* The MovementConfig to use for this mount
* **Type**: `String`, **Required**, **Computable**
[(Top)](#top)
## OpenBarterShop: Action (Stable)
Open the barter shop UI for the current player
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Shop (Stable)
* The barter shop to open
* **Type**: `Asset`, **Required**, **Computable**
### Constraints
[(Top)](#top)
## OpenShop: Action (Stable)
Open the shop UI for the current player
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Shop (Stable)
* The shop to open
* **Type**: `Asset`, **Required**, **Computable**
### Constraints
[(Top)](#top)
## StartObjective: Action (Stable)
Start the given objective for the currently iterated player in the interaction instruction
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Objective (Stable)
* The task to start
* **Type**: `Asset`, **Required**, **Computable**
### Constraints
[(Top)](#top)
# List of Core Components (path)
## MakePath: Action (WorkInProgress)
Constructs a transient path for the NPC based on a series of rotations and distances
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Path (WorkInProgress)
* A transient path definition
* **Type**: `ObjectRef`, **Required**, **Object Type**: Path
[(Top)](#top)
## Path: Sensor (Stable)
Find a path based on various criteria. Provides the position of the nearest waypoint and the path itself
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Path (Stable)
* The name of the path. If left blank, will find the nearest path
* **Type**: `String`, **Optional** (Default: null), **Computable**
#### - Range (Stable)
* The range to test to nearest waypoint. 0 is unlimited
* **Type**: `Double`, **Optional** (Default: 10.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - PathType (Stable)
* The type of path to search for
* **Type**: `Flag`, **Optional** (Default: AnyPrefabPath), **Computable**
* **Flag Values**:
* `CurrentPrefabPath`: a path from the prefab the NPC spawned in
* `TransientPath`: a transient path (testing purposes only)
* `AnyPrefabPath`: a path from any prefab
* `WorldPath`: named world path
### Constraints
* If PathType is WorldPath, the following must apply to Path: String must be not empty
### Provides
* Vector position
* Path
[(Top)](#top)
# List of Core Components (statemachine)
## IsBusy: Sensor (Stable)
Tests if an NPC is in one of the defined Busy States.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## ParentState: Action (Stable)
Set the main state of NPC from within a component
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - State (Stable)
* The alias of the external state to set, as defined by \_ImportStates in parameters
* **Type**: `String`, **Required**
* **Constraint**: String must be a valid state string. A main state must be included before the period (e.g. Main.Test). Only the main state can be included.
### Constraints
* May only be included within a component
[(Top)](#top)
## State: Action (Stable)
Set state of NPC. The state can be queried with a sensor later on.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - State (Stable)
* State name to set
* **Type**: `String`, **Required**
* **Constraint**: String must be a valid state string. State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
#### - ClearState (Stable)
* Clear the state of things like set once flags on transition
* **Type**: `Boolean`, **Optional** (Default: true)
### Constraints
[(Top)](#top)
## State: Sensor (Stable)
Signal if NPC is set to specific state.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - State (Stable)
* State to compare to
* **Type**: `String`, **Required**
* **Constraint**: String must be a valid state string. State strings consist of a main state and a sub state (e.g. Main.Test). If nested within a substate, the main state may be omitted (e.g. .Test) when referencing.
#### - IgnoreMissingSetState (Stable)
* Override and ignore checks for matching setter action that sets this state. Intended for use in cases such as the FlockState action which sets the state via another NPC
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## ToggleStateEvaluator: Action (Stable)
Enable or disable the NPC's state evaluator
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether or not to enable the state evaluator
* **Type**: `Boolean`, **Required**
[(Top)](#top)
# List of Core Components (timer)
## Alarm: Sensor (Stable)
Check the state of a named alarm and optionally clear it if the time has passed
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Name (Stable)
* The name of the alarm to check
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - State (Stable)
* The state to check for
* **Type**: `Flag`, **Required**, **Computable**
* **Flag Values**:
* `SET`: Set
* `UNSET`: Not set
* `PASSED`: Passed
#### - Clear (Stable)
* Whether to clear the alarm (unset it) if it has passed
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
[(Top)](#top)
## SetAlarm: Action (Stable)
Set a named alarm on the NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the alarm to set
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - DurationRange (Stable)
* The duration range from which to pick a duration to set the alarm for. \[ "P0D", "P0D" ] will unset the alarm
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: TemporalAmount
* **Constraint**: Values must be greater or equal than a few seconds, less or equal than 5879611 years, in weakly ascending order and either all Periods or all Durations
[(Top)](#top)
## Timer: Sensor (Stable)
Tests if a timer exists and the value is within a certain range.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - State (Stable)
* The timer's state to check
* **Type**: `Flag`, **Optional** (Default: ANY)
* **Flag Values**:
* `PAUSED`: Paused
* `RUNNING`: Running
* `STOPPED`: Stopped
* `ANY`: Any
#### - TimeRemainingRange (Stable)
* The acceptable remaining time on the timer.
* **Type**: `Array`, **Optional** (Default: \[0.0, 1.7976931348623157E308]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
[(Top)](#top)
## TimerContinue: Action (Stable)
Continue a timer
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## TimerModify: Action (Stable)
Modify values of a timer
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - AddValue (Stable)
* Add value to the timer
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - MaxValue (Stable)
* Set the restart value range the timer can have. If \[ 0, 0 ] (default) it will be ignored
* **Type**: `Array`, **Optional** (Default: \[0.0, 0.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308
#### - Rate (Stable)
* Set the rate at which the timer will decrease. If 0 (default) it will be ignored
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - SetValue (Stable)
* Set the value of the timer. If 0 (default) it will be ignored
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - Repeating (Stable)
* Whether to repeat the timer when countdown finishes
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
[(Top)](#top)
## TimerPause: Action (Stable)
Pause a timer
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## TimerRestart: Action (Stable)
Restart a timer. Will be set to the original initial values.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## TimerStart: Action (Stable)
Start a timer
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - StartValueRange (Stable)
* The range from which to pick an initial value to start at
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - RestartValueRange (Stable)
* The range from which to pick a value when the timer is restarted. The upper bound is also the timer max
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - Rate (Stable)
* The rate at which the timer will decrease
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - Repeating (Stable)
* Whether to repeat the timer when countdown finishes
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
[(Top)](#top)
## TimerStop: Action (Stable)
Stop a timer
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the timer
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
# List of Core Components (utility)
## AdjustPosition: Sensor (Stable)
Perform adjustments to the wrapped sensor's returned position
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor to wrap
* **Type**: `ObjectRef`, **Required**, **Object Type**: Sensor
#### - Offset (Stable)
* The offset to apply to the returned position from the sensor
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
### Provides
* Vector position
[(Top)](#top)
## And: Sensor (Stable)
Evaluate all sensors and execute action only when all sensor signal true. Target is provided by first sensor.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensors (Stable)
* List of sensors
* **Type**: `Array`, **Required**, **Element Type**: Sensor
* **Constraint**: Array must not be empty
#### - AutoUnlockTargetSlot (Stable)
* A target slot to unlock when sensor doesn't match anymore
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
[(Top)](#top)
## Any: Sensor (Stable)
Sensor always signals true but doesn't return a target.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## Eval: Sensor (Experimental)
Evaluate javascript expression and test truth value. Current values accessible are 'health' and 'blocked'.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Expression (Experimental)
* Javascript expression
* **Type**: `String`, **Required**
* **Constraint**: String must be not empty
[(Top)](#top)
## Flag: Sensor (Stable)
Test if a named flag is set or not
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Name (Stable)
* The name of the flag
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Set (Stable)
* Whether the flag should be set or not
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## Not: Sensor (WorkInProgress)
Return true when the given sensor test fails.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor to test
* **Type**: `ObjectRef`, **Required**, **Object Type**: Sensor
#### - UseTargetSlot (Stable)
* A locked target slot to feed to action (if available)
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - AutoUnlockTargetSlot (Stable)
* A target slot to unlock when sensor doesn't match anymore
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
[(Top)](#top)
## Nothing: Action (Stable)
Do nothing. Used often as placeholder.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## Or: Sensor (Stable)
Evaluate sensors and execute action when at least one sensor signals true. Target is provided by first sensor signalling true.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensors (Stable)
* List of sensors
* **Type**: `Array`, **Required**, **Element Type**: Sensor
* **Constraint**: Array must not be empty
#### - AutoUnlockTargetSlot (Stable)
* A target slot to unlock when sensor doesn't match anymore
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
[(Top)](#top)
## Random: Action (Stable)
Execute a single random action from a list of weighted actions.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Actions (Stable)
* List of possible actions
* **Type**: `Array`, **Required**, **Element Type**: WeightedAction
[(Top)](#top)
## Random: Sensor (Stable)
Alternates between returning true and false for specified random durations
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - TrueDurationRange (Stable)
* The duration range to pick a random period to return true
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - FalseDurationRange (Stable)
* The duration range to pick a random period to return false
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
[(Top)](#top)
## ResetInstructions: Action (Stable)
Force reset instructionList, either by name, or as a whole
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Instructions (Stable)
* The instructionList to reset. If left empty, will reset all instructionList
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
[(Top)](#top)
## Sequence: Action (Stable)
Execute list of actions.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Blocking (Stable)
* Do not execute an action unless the previous action could execute
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Atomic (Stable)
* Only execute actions if all actions can be executed
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Actions (Stable)
* List of actions
* **Type**: `ObjectRef`, **Required**, **Object Type**: ActionList
[(Top)](#top)
## SetFlag: Action (Stable)
Set a named flag to a boolean value
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Name (Stable)
* The name of the flag
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - SetTo (Stable)
* The value to set the flag to
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## Switch: Sensor (Stable)
Check if a computed boolean is true
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Switch (Stable)
* The switch to check
* **Type**: `Boolean`, **Required**, **Computable**
[(Top)](#top)
## Timeout: Action (Stable)
Delay an action by a time which is randomly picked between a given minimum and maximum value.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Delay (Stable)
* Range of time to delay in seconds
* **Type**: `Array`, **Optional** (Default: \[1.0, 1.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 1.7976931348623157e+308and in weakly ascending order
#### - DelayAfter (Stable)
* Delay after executing the action
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Action (Stable)
* Optional action to delay
* **Type**: `ObjectRef`, **Optional** (Default: null), **Object Type**: Action
[(Top)](#top)
## ValueProviderWrapper: Sensor (Stable)
Wraps a sensor and passes down some additional parameter overrides pulled from the value store
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - PassValues (Stable)
* Used to enable/disable passing of values in components
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor to wrap
* **Type**: `ObjectRef`, **Required**, **Object Type**: Sensor
#### - ValueToParameterMappings (Stable)
* The mappings of values to override parameters
* **Type**: `Array`, **Required**, **Element Type**: ValueToParameterMapping
* **Constraint**: Array must not be empty
[(Top)](#top)
# List of Core Components (world)
## Block: Sensor (Experimental)
Checks for one of a set of blocks in the nearby area and caches the result until explicitly reset or the targeted block changes/is removed. All block sensors with the same sought blockset share the same targeted block once found
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Range (Stable)
* The range to search for the blocks in
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1.7976931348623157e+308
#### - MaxHeight (Stable)
* The vertical range to search for the blocks in
* **Type**: `Double`, **Optional** (Default: 4.0), **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 1.7976931348623157e+308
#### - Blocks (Stable)
* The set of blocks to search for
* **Type**: `Asset`, **Required**, **Computable**
#### - Random (Stable)
* Whether to pick at random from within the matched blocks or pick the closest
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
#### - Reserve (Stable)
* Whether to reserve the found block to prevent other NPCs selecting it
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Provides
* Vector position
[(Top)](#top)
## BlockChange: Sensor (Stable)
Matches when a block from a blockset within a certain range is changed or interacted with
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Range (Stable)
* Max range to listen in
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - SearchType (Stable)
* Whether to listen for events triggered by players, npcs, or both in a certain order
* **Type**: `Flag`, **Optional** (Default: PlayerOnly), **Computable**
* **Flag Values**:
* `NpcOnly`: search only for events triggered by npcs
* `PlayerFirst`: search for events triggered by players first
* `PlayerOnly`: search only for events triggered by players
* `NpcFirst`: search for events triggered by npcs first
#### - TargetSlot (Stable)
* A target slot to place the target in. If omitted, no slot will be used
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - BlockSet (Stable)
* Block set to listen for
* **Type**: `Asset`, **Required**, **Computable**
#### - EventType (Stable)
* The event type to listen for
* **Type**: `Flag`, **Optional** (Default: DAMAGE), **Computable**
* **Flag Values**:
* `DESTRUCTION`: On block destruction
* `INTERACTION`: On block use interaction
* `DAMAGE`: On block damage
### Provides
* Player target
* NPC target
[(Top)](#top)
## BlockType: Sensor (Stable)
Checks if the block at the given position matches the provided block set
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Sensor (Stable)
* Sensor to wrap
* **Type**: `ObjectRef`, **Required**, **Object Type**: Sensor
#### - BlockSet (Stable)
* Block set to check against
* **Type**: `Asset`, **Required**, **Computable**
[(Top)](#top)
## CanPlaceBlock: Sensor (Stable)
Test if the currently set block can be placed at the relative position given direction and offset
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Direction (Stable)
* The direction to place relative to heading
* **Type**: `Flag`, **Optional** (Default: Forward), **Computable**
* **Flag Values**:
* `Left`: Left
* `Backward`: Backward
* `Right`: Right
* `Forward`: Forward
#### - Offset (Stable)
* The offset to place at
* **Type**: `Flag`, **Optional** (Default: BodyPosition), **Computable**
* **Flag Values**:
* `BodyPosition`: BodyPosition
* `FootPosition`: FootPosition
* `HeadPosition`: HeadPosition
#### - RetryDelay (Stable)
* The amount of time to delay if a placement fails before trying to place something again
* **Type**: `Double`, **Optional** (Default: 5.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AllowEmptyMaterials (Stable)
* Whether it should be possible to replace blocks that have empty material
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Provides
* Vector position
[(Top)](#top)
## EntityEvent: Sensor (Stable)
Matches when an entity from a specific NPC group within a certain range is damaged, killed, or interacted with
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Range (Stable)
* Max range to listen in
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - SearchType (Stable)
* Whether to listen for events triggered by players, npcs, or both in a certain order
* **Type**: `Flag`, **Optional** (Default: PlayerOnly), **Computable**
* **Flag Values**:
* `NpcOnly`: search only for events triggered by npcs
* `PlayerFirst`: search for events triggered by players first
* `PlayerOnly`: search only for events triggered by players
* `NpcFirst`: search for events triggered by npcs first
#### - TargetSlot (Stable)
* A target slot to place the target in. If omitted, no slot will be used
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
#### - NPCGroup (Stable)
* NPC group to listen for
* **Type**: `Asset`, **Required**, **Computable**
#### - EventType (Stable)
* The event type to listen for
* **Type**: `Flag`, **Optional** (Default: DAMAGE), **Computable**
* **Flag Values**:
* `DEATH`: On dying
* `INTERACTION`: On use interaction
* `DAMAGE`: On taking damage
#### - FlockOnly (Stable)
* Whether to only listen for flock events
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Provides
* Player target
* NPC target
[(Top)](#top)
## InWater: Sensor (Stable)
Check if NPC is currently in water
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
[(Top)](#top)
## Leash: Sensor (Stable)
Triggers when the NPC is outside a specified range from the leash point
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Range (Stable)
* The farthest distance allowed from the leash point
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
### Provides
* Vector position
[(Top)](#top)
## Light: Sensor (Stable)
Check the light levels of the block an entity is standing on. Can test light intensity, sky light or block channel levels.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - LightRange (Stable)
* The light intensity percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - SkyLightRange (Stable)
* The sky light percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - SunlightRange (Stable)
* The sunlight percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - RedLightRange (Stable)
* The red light percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - GreenLightRange (Stable)
* The green light percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - BlueLightRange (Stable)
* The blue light percentage range
* **Type**: `Array`, **Optional** (Default: \[0.0, 100.0]), **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 100and in weakly ascending order
#### - UseTargetSlot (Stable)
* A target slot to check. If omitted, will check self
* **Type**: `String`, **Optional** (Default: null), **Computable**
* **Constraint**: String value must be either null or not empty
[(Top)](#top)
## PlaceBlock: Action (Stable)
Place a block (chosen by another action) at a position returned by a Sensor if close enough
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Range (Stable)
* The range to target position before block will be placed
* **Type**: `Double`, **Optional** (Default: 3.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - AllowEmptyMaterials (Stable)
* Whether it should be possible to replace blocks that have empty material
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Constraints
* Must be attached to a sensor that provides one of vector position
[(Top)](#top)
## ReadPosition: Sensor (Stable)
Read a stored position with some conditions
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Slot (Stable)
* The slot to read the position from
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - MinRange (Stable)
* Minimum range from stored position
* **Type**: `Double`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
#### - Range (Stable)
* Maximum range from stored position
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - UseMarkedTarget (Stable)
* Whether to read from a marked target slot instead of a position slot
* **Type**: `Boolean`, **Optional** (Default: false), **Computable**
### Provides
* Vector position
[(Top)](#top)
## ResetBlockSensors: Action (Stable)
Resets a specific block sensor by name, or all block sensors by clearing the current targeted block
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - BlockSets (Stable)
* The searched blocksets to reset block sensors for. If left empty, will reset all block sensors and found blocks
* **Type**: `AssetArray`, **Optional** (Default: null), **Computable**, **Element Type**: BlockSet
[(Top)](#top)
## ResetPath: Action (Stable)
Resets the current patrol path this NPC follows.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
[(Top)](#top)
## ResetSearchRays: Action (Stable)
Resets a specific search ray sensor cached position by name, or all search ray sensors
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Names (Stable)
* The search ray sensor ids. If left empty, will reset all search ray sensors
* **Type**: `Array`, **Optional** (Default: null), **Computable**, **Element Type**: String
* **Constraint**: Strings in array must not be empty
[(Top)](#top)
## SearchRay: Sensor (Stable)
Fire a ray at a specific angle to see if what it hits matches a given sought block
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Name (Stable)
* The id of this search ray sensor so the position can be cached
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
#### - Angle (Stable)
* Angle to fire the ray. Horizontal is 0. Positive is downwards
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater or equal than -90 and less or equal than 90
#### - Range (Stable)
* How far to search
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0 and less or equal than 96
#### - Blocks (Stable)
* The blockset to search for
* **Type**: `Asset`, **Required**, **Computable**
#### - MinRetestAngle (Stable)
* The minimum change in NPC rotation before rays stop being throttled
* **Type**: `Double`, **Optional** (Default: 5.0), **Computable**
* **Constraint**: Value must be greater or equal than 0 and less or equal than 360
#### - MinRetestMove (Stable)
* The minimum distance the NPC needs to move while facing the same direction before rays stop being throttled
* **Type**: `Double`, **Optional** (Default: 1.0), **Computable**
* **Constraint**: Value must be greater than 0
#### - ThrottleTime (Stable)
* The delay between retests when an NPC is facing the same direction
* **Type**: `Double`, **Optional** (Default: 0.5), **Computable**
* **Constraint**: Value must be greater than 0
### Provides
* Vector position
[(Top)](#top)
## SetBlockToPlace: Action (Stable)
Set the block type the NPC will place
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Block (Stable)
* The block item type
* **Type**: `Asset`, **Required**, **Computable**
[(Top)](#top)
## SetLeashPosition: Action (Stable)
Sets the NPCs current position to the spawn/leash position to be used with the Leash Sensor.
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ToCurrent (Stable)
* Set to the NPCs current position.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ToTarget (Stable)
* Set to the target position.
* **Type**: `Boolean`, **Optional** (Default: false)
### Constraints
* At least one of ToCurrent, ToTarget must be true
* If ToTarget is true, must be attached to a sensor that provides one of player target, NPC target, dropped item target
[(Top)](#top)
## StorePosition: Action (Stable)
Store the position from the attached sensor
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Slot (Stable)
* The slot to store the position in
* **Type**: `String`, **Required**, **Computable**
* **Constraint**: String must be not empty
[(Top)](#top)
## Time: Sensor (Stable)
Check if the day/year time is within some specified time. If you want to check a range of time which crosses through midnight and switches to the next day, use the greater time as the min value and the lesser value as the max value.
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Period (Stable)
* The time period to trigger within
* **Type**: `Array`, **Required**, **Computable**, **Element Type**: Double
* **Constraint**: Values must be greater or equal than 0, less or equal than 24
#### - CheckDay (Stable)
* Check the day time. When using a double the values go from \[.00, .99]. Don't get confused with there only being 60 minutes in an hour.
* **Type**: `Boolean`, **Optional** (Default: true)
#### - CheckYear (WorkInProgress)
* Check the year time. When using a double the values go from \[.00, .99]. Don't get confused with there only being 60 minutes in an hour.
* **Type**: `Boolean`, **Optional** (Default: false)
#### - ScaleDayTimeRange (Stable)
* Whether to use a relative scale for the day time. Sunrise will be at relative 6, Noon at 12, and Sunset at 18, regardless of actual in-game time
* **Type**: `Boolean`, **Optional** (Default: true)
[(Top)](#top)
## TriggerSpawners: Action (Stable)
Trigger all, or up to a certain number of manual spawn markers in a radius around the NPC
### Attributes
#### - Once (Stable)
* Execute only once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - SpawnMarker (Stable)
* The spawn marker type to trigger
* **Type**: `Asset`, **Optional** (Default: null), **Computable**
#### - Range (Stable)
* The range within which to trigger spawn markers
* **Type**: `Double`, **Required**, **Computable**
* **Constraint**: Value must be greater than 0
#### - Count (Stable)
* The number of markers to randomly trigger (0 will trigger all matching validators)
* **Type**: `Integer`, **Optional** (Default: 0.0), **Computable**
* **Constraint**: Value must be greater or equal than 0
[(Top)](#top)
## Weather: Sensor (Stable)
Matches the current weather at the NPCs position against a set of weather globs
### Attributes
#### - Once (Stable)
* Sensor only triggers once
* **Type**: `Boolean`, **Optional** (Default: false)
#### - Enabled (Stable)
* Whether this sensor should be enabled on the NPC
* **Type**: `Boolean`, **Optional** (Default: true), **Computable**
#### - Weathers (Stable)
* The glob patterns to match against weather
* **Type**: `AssetArray`, **Required**, **Computable**, **Element Type**: Weather
[(Top)](#top)
--- Introduction
Source: https://hytalemodding.dev/en/docs/official-documentation
All of the documentation under the "Official Documentation" category is provided to us by Hypixel Studios Canada, Inc.
This documentation is provided as-is and may be subject to change without notice.
If you've found a issue with this documentation, please create a ticket on our [Discord Server](https://discord.gg/hytalemodding) and we will look into it. You can also email us at `hello@hytalemodding.dev` for such requests.
--- Frequently Asked Questions
Source: https://hytalemodding.dev/en/docs/established-information/faq
On this page, we'll answer some of your Frequently Asked Questions.
## 1. Can the client be modded?
No, the client is not able to be modded, although the Client will not have control over how the game and the UI looks.
The server decides the visuals, more like the "Resource Packs" available in Minecraft. The server decides what mods are used
## 2. What Network Protocol will be used?
Hytale will use the QUIC transport protocol.
For context, the two main Internet protocols are UDP and TCP.
* UDP is connectionless, fast, fire-and-forget packets but has less reliability as packets can be lost.
* TCP is connection-based, slower than UDP, can guarantee packet delivery with much more reliability.
Both are pretty fast in modern times, games can run fine on both (but generally prefer UDP).
QUIC is a **hybrid protocol** that builds off UDP to get the same speed benefits, but adds some layers of reliability like TCP.
Essentially, a solid choice for game development that blends speed and reliability.
## 3. What are Transfer Packets?
Transfer packets in Hytale are small data payloads *(4kb)* that allow players to move from one server to another while carrying information.
They are not equivalent to something like BungeeCord or Velocity and are primarily intended for smaller servers.
Unlike BungeeCord, they do not manage network-wide features such as global plugins.
Implementing DDOS protection would also be incredibly hard for smaller networks by using transfer packets.
Transfer packets work quite well in scenarios such as small server networks or hubs where players will hop between servers without the loss of progress. For example, a player might finish a dungeon on one server and then move to a lobby or different game mode with their inventory and stats intact. This lessens server costs and enables the developers to put more effort into gameplay improvement rather than complicated network setups.
## 4. Will Hytale support dedicated servers?
Yes, Hytale will support dedicated servers. This allows server owners to host their own Hytale servers on their own hardware or through third-party hosting services.
Dedicated servers provide more control over server settings, mods, and performance compared to using official hosting solutions.
## 5. Will Hytale support modding on consoles?
As of now, Hytale modding is primarily focused on PC platforms.
--- Developer Q&A Technical Insights
Source: https://hytalemodding.dev/en/docs/established-information/developer-qa-insights
# Developer Q\&A Technical Insights
This extensive Q\&A session with Hytale developers reveals critical technical details about the game's architecture, modding philosophy, gameplay systems, and development approach.
## Technical Architecture
### Network & Infrastructure
* **Protocol**: Uses QUIC (reliable UDP with unreliable channels) instead of TCP
* **Server Compatibility**: Runs on any platform supporting Java 25
* **Connection Method**: Direct IP initially, server discovery planned post-launch
* **Data Layer**: Current custom serialization format will be replaced post-launch
* **Performance**: Multi-core support - each world has main thread + parallel execution
* **Rendering**: OpenGL 3.3 for Mac compatibility, potential future Vulkan/Metal migration
* **Tick Model**: Base server tick rate is 30 TPS by default; can be changed using server plugins.
### Security & Distribution
* **No Client-Side Code Execution**: Prevents remote code execution exploits
* **Downloads Limited To**: Configuration files and assets only
* **Mod Distribution**: Community platforms (CurseForge, Modrinth), no immediate in-game marketplace
* **Server Hosting**: Available day 1, no business agreements required (just EULA/TOS)
## Modding Capabilities
### Server-Side Power
* **Full Java Access**: Database connections, web requests, any Java functionality
* **Custom Libraries**: Can create Lua scripting, ML frameworks, or any Java library
* **Server Modification**: Shared source code allows heavy server modifications
* **Hot Reload**: Supported for most asset types
* **Documentation**: Public GitBook planned, though initially sparse
### Content Creation (No Code Required)
* **World Generation**: Custom biomes and maps without Java
* **NPCs & Items**: JSON-based configuration
* **Crafting Recipes**: Data-driven system
* **Visual Scripting**: Replaces command blocks, fully sandboxed
* **Prefab System**: Sophisticated building blocks with in-game editor
### Advanced Modding
* **Combat System**: Data-driven interaction system for attack chains and abilities
* **Sub-Hitboxes**: Support for complex boss mechanics (needs improvement)
* **Status Effects**: Buff/debuff system with custom effects and stats
* **Custom UI**: NoesisGUI transition enables powerful interface customization
* **Block Physics**: Selective system (trees fall, but placed blocks don't)
## Gameplay Systems
### Core Mechanics
* **Stamina System**: No hunger by default (can be modded in)
* **Gravity**: Selective block physics - trees collapse, buildings can float
* **Lighting**: Major overhaul to fix model/voxel inconsistencies
* **World Height**: Currently limited, may change with voxel storage updates
* **Automation**: No redstone equivalent at launch (moddable)
### Player Systems
* **Character Models**: Humans by default, changeable in creative mode
* **Multiplayer Avatars**: Server-controlled (no custom models in multiplayer)
* **Skins**: Full customization possible ("mod yourself into a potato")
* **Shapeshifting**: Transformation potions exist for fun/modding
### World & NPCs
* **Faction System**: Living world with dynamic NPC interactions
* **Village Systems**: Planned dynamic villages with relationships and behaviors
* **Chunk Loading**: Entities unload when out of all players' render distance
* **Seeds**: Shareable world generation seeds supported
## Development Philosophy
### Release Strategy
* **Base Game Priority**: Adventure mode is primary focus, modding architecture supports it
* **Tech Debt**: Acknowledged as necessary for release, commitment to improvement
* **Update Frequency**: Very frequent updates planned, especially post-launch
* **Version Policy**: Always latest version, but servers can run modified versions
### Community Focus
* **Creator Support**: Development bounties planned for community contributors
* **Tutorials**: Official modding tutorials planned with partners
* **Feedback Integration**: Strong commitment to community-driven improvements
* **Accessibility**: Designed for lower-end hardware, reasonable system requirements
### Tools & Documentation
* **Blockbench Integration**: Replaces custom "Hytale Model Maker"
* **Machinima Tools**: Full cinematic creation capabilities
* **Creative Tools**: Major reveal planned for building/creation features
* **Source Access**: Server code will be unobfuscated with developer comments
## Platform Support
* **Current**: Windows (primary)
* **Planned**: Mac/Linux support actively in development
* **VR**: Not feasible in near future
* **Performance**: Uncapped framerate, designed for accessibility
* **Offline Play**: Single-player works without internet connection
## Monetization & Legal
* **No Pay-to-Win**: Firm commitment against P2W mechanics
* **Cosmetics Only**: Low-price cosmetic packs to support infrastructure
* **Server EULA**: Terms being finalized for server monetization policies
* **QUIC Blocking**: Acknowledged issue in some regions, no alternative protocol
## Current Limitations
* **Custom Shaders**: Not supported until sandboxing solution found
* **UI/Input Limitations**: Being expanded but currently restricted
* **Documentation**: Sparse initially, will improve over time
* **Client Mods**: No client-side code execution for security
* **Nameplates**: Currently limited customization
This comprehensive technical foundation positions Hytale as a secure, extensible platform that empowers creators while maintaining performance, accessibility, and a commitment to continuous improvement.
--- Server Code Insights
Source: https://hytalemodding.dev/en/docs/established-information/server-plugin-insights
The following information is based on insights shared by Technical Director Slikey on December 15, 2025 on the Hytale Discord Server. It provides an overview of the server code architecture and plugin capabilities.
Majority of this document is just straight quotes from Slikey with minimal commentary. It is more over to preserve the information shared rather than reword it.
# Server Source Code
The server will be released as shared source - we will have the source code with no obfuscation and all their comments.
### License
The license permits usage for any **hytale related** things, so as long as you don't use the server / source to make your own game or sell their algorithms.
# Permission system
After being asked in the Hytale Modding server, Lead Architect Zero has replied:
It's quite basic, so don't expect much but yes we do have a permissions system built in. It also support implementing your own backend. Ex. if you wanted to store it in a database.
# World Gen Implementation
The following block of text is directly from Lead Architect Zero:
You can totally implement a custom world gen in a plugin super easy. So if you want a totally different config or logic for generating terrain you can add that into a plugin and then configure a world to use that,
We have some stuff load prefabs and there is existing code in the current world generators that could possibly be reused but no APIs specifically for the details of the generation itself.
```java
public interface IWorldGenProvider {
BuilderCodecMapCodec CODEC = new BuilderCodecMapCodec<>("Type", true);
IWorldGen getGenerator() throws WorldGenLoadException;
}
```
```java
public interface IWorldGen {
@Nullable
WorldGenTimingsCollector getTimings();
CompletableFuture generate(int seed, long index, int x, int z, LongPredicate stillNeeded);
@Deprecated
Transform[] getSpawnPoints(int seed);
@Nonnull
default ISpawnProvider getDefaultSpawnProvider(int seed) {
return new FitToHeightMapSpawnProvider(new IndividualSpawnProvider(getSpawnPoints(seed)));
}
default void shutdown() {}
}
```
# Server Code Snippets
Some code snippets from a plugin they made were also shared. The following snippets are directly quoted from Technical Director Slikey:
## BlockSpawner Plugin
Alright, I'll drop some code snippets here for a simple plugin called "BlockSpawner" which will turn into a "random" block when put into the world based on the configured rules
There is more code here in general but it shows the Asset System and the plugin layout. You can see that the asset system defines "Codecs" which are used to serialize and deserialize data. Using these codecs we can generate schemas.
Let's say you connect to a server and open the asset editor: the server will send those schemas - so every single asset type defined server side, even you own, now become part of the asset editor on the client - no need to do anything, you just get to enjoy the editor UI for making your assets that you defined
> i guess this part also shows a bit how we use constants to store some language keys - Slikey
> (in reference to the image below)
> you can completely customize the chunk storage provider, we for example made one that is just empty where it doesn't load any chunks from disk
> you could add an MySQLChunkStorageProvider and it could read the data from a database
> and the same goes for worldgen, you can simply implement this interface to generate a world generator - slikey
> i mean here are all the weapons defined, you tell me, obviously you will discover limitations but i think there is plenty of room to make changes - this stuff is actually hard though, there is a lot of QoL to have happen here
--- PR Guidelines
Source: https://hytalemodding.dev/en/docs/contributing/pr-guidelines
# Pull Request Guidelines
To ensure a smooth and efficient review process, both contributors and reviewers should adhere to the following guidelines when submitting and reviewing pull requests (PRs).
## For Contributors
1. **Descriptive Titles and Descriptions**
* Use clear and concise titles that summarize the changes.
* Provide detailed descriptions of what the PR does, why the changes are necessary, and any relevant context.
2. **Link to Issues**
* In your PR title, add `GH-ISSUE-NUMBER` to link the PR to the relevant issue. This is required for all PRs.
3. **Small, Focused Changes**
* Aim to keep PRs small and focused on a single issue or feature. This makes it easier for reviewers to understand and provide feedback.
4. **Code Quality**
* Ensure your code follows the project's coding standards and best practices.
* Include comments where necessary to explain complex logic.
* Always test your changes locally before submitting the PR to catch any issues early.
* Format your code using `bun format` to maintain consistency across the codebase.
5. **Testing**
* Test your changes thoroughly before submitting the PR.
* If applicable, include unit tests or integration tests to verify your changes.
6. **Documentation**
* Update or add documentation as needed to reflect your changes.
7. **Commit Messages**
* Use clear and descriptive commit messages that follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) format.
8. **Check for Conflicts**
* Before submitting your PR, ensure that your branch is up to date with the base branch to minimize merge conflicts.
## For Reviewers
* You are supposed to review PRs thoroughly, and request changes if needed.
* Provide constructive feedback and suggestions for improvement.
* Verify that the changes meet the project's coding standards and best practices.
* Deploy the PR using the dokploy integration to ensure the changes work as expected.
* Ensure that documentation is updated as necessary.
* Approve the PR only when you are satisfied with the changes and all checks have passed.
--- Writing Guides
Source: https://hytalemodding.dev/en/docs/contributing/writing-guides
## Creating a guide file
To start writing a guide, you need to create a file with extension `.mdx`. You need to create this file inside the `/content/docs/en` directory, now it depends in which section you want to make the guide, so you can decide which folder you should put it in after that.
Suppose I want to make a java basics guide, I'd put it in `/content/docs/en/guides/java-basics`. You'll have to do some digging to find out where you want to place it.
If there's already a guide in the section you want to write about, you can just press on the "Edit On GitHub" button on our website and it'll take you to the folder, now you can write in the same folder to have your guide appear in the same section.
## Frontmatter
Frontmatter is the metadata of your guide, it tells our website how to display your guide. This needs to be at the very top of the file, which starts with `---` and ends with `---`.
Here's an example:
```mdx
---
title: "Java Basics"
description: "Learn the basics of Java programming."
authors:
- name: "Your Name"
url: "https://yourwebsite.com"
icon: YourIcon
---
```
Now as you can see, it's pretty self explanatory, you just need to fill in the details for your specific guide. If you don't have a URL you want to link yourself to, just don't write that whole line. Additionally, the authors section is optional, so if you don't want to add it, you can just leave it out although we encourage you to do so and credit yourself.
The indentation in the frontmatter is important, so make sure to keep it consistent with the example.
## Content Structure
You should structure the content of your guide in a clear and organized manner. Here are some tips on how to do that:
* **Introduction:** Start with an overview of the topic and what the reader can expect to learn.
* **Prerequisites:** List any prerequisites or prior knowledge the reader should have before diving into the guide.
* **Step-by-Step Instructions:** Break down the content into clear, actionable steps. Use headings and subheadings to organize the information.
* **Code Examples:** Include code snippets to illustrate key points. Make sure to explain each example thoroughly.
* **Conclusion:** Summarize the main points and encourage the reader to explore further or try out what they've learned.
Now if you're writing a really big section, you should split this into multiple pages so that it's easier for the reader to digest the information. No one wants to read a big wall of text all at once!
When we write guides, we should also think of how people will consume them, and many people prefer to skim content rather than read it in detail. This is why it's better to split information into multiple pages, so it looks like a smaller amount of information to the reader. Also, consider using bullet points, numbered lists, and other formatting techniques to make the content more scannable.
## Formatting
We use [Markdown](https://www.markdownguide.org/) for formatting our guides. Here are some key points to keep in mind:
* Use headings (`#`, `##`, `###`) to organize your content.
* Use bullet points and numbered lists for clarity.
* Use code blocks (\`\`\`) for code snippets.
* Use links to reference other guides or external resources.
This is the same type of formatting you'll see on most documentation websites, and also social media platforms like Discord.
### Links
Everyone knows what a link is. However, there is a difference between linking to an external resource and linking to an internal one.
In general, in Markdown they are written like this: `[Your Text](Your link)`
#### External
Nothing to explain, just `https://example.com`.
#### Internal
You cannot use regular links here. Let's say your link to another guide is `https://hytalemodding.dev/en/docs/quick-start`.
You can't write it like that because you are not only referring to the page but also to the language, which is not correct.
Instead, type `./quick-start` which is correct and links to the page in the user's language.
**Accessing files**
* **Current Folder**: `./`
* **Folder above**: `../` as many times as this folder is higher in the hierarchy.
## Icons
If you want to add an icon for your page then you should use [Lucide](https://lucide.dev/).
1. Go to the [site](https://lucide.dev/) and find an icon you like.
2. Once you have selected an icon, click on it, then click "See in action"
3. Next, click on "React" which we are using. You will only need the text in the curly brackets on the first line.
4. Next, simply paste `icon: YourIcon` into Frontmatter.
## Callout
You can also make a Callout to highlight important information or tips. Use the following syntax:
```mdx
This is a callout message.
```
This is a callout message.
You can change the color of the callout by adding a `type` attribute. For example:
```mdx
This is an info callout.
```
Available types are: `info`, `warning`, `error`, and `success`.
## Difficulty levels
These are types of callouts, they are optional, they show how complex your page can be.
There are 3 main levels of complexity.
Use `lvl_beginner`. This page contains information for those who are starting out with modding.
Use `lvl_intermediate`. This page contains information for those who want to deepen their knowledge.
Use `lvl_advanced`. This page contains information for those who already know how to make quality mods, and contains something additional.
Their usage is the same as in callout, but we would advise not to change the title. Inside,
you write why your page is classified as this level and what the user should know.
## Conclusion
That's all! You are now a master at writing guides. Just remember, do not think you need to be perfect with your English, write the guide the way you will explain the topic to someone, and don't hesitate to ask for feedback or help if you need it.
Happy writing!
--- Translate
Source: https://hytalemodding.dev/en/docs/contributing/translate
# Translating HytaleModding
HytaleModding is translated with the help of the community using **Crowdin**. If you want to help translate or improve existing translations, this is the right place.
## Steps to Translate
All translations are managed **via Crowdin**.
1. Visit [our Crowdin project](https://crowdin.com/project/hytalemodding) and log in if required.
2. Click on your language.
3. Click on the file/article you wish to translate. We recommend starting with Official Documentation, and then Server Plugin Documentation.
4. Start translating!
5. Approved translations will appear on the website.
### Video Guide
## How to discuss
You can always report issues or discuss specific topics directly on Crowdin, but we recommend using Discord instead of the Crowdin comment system to keep everything organized in one place.
1. Join [our Discord](https://discord.gg/hytalemodding)
2. Open `#translations` channel. Here you can discuss or ask questions that are not related to a specific language.
3. If you want to join a thread, you are required to run `/translator ` command here. The bot will add you to the thread for your language.
## Translation Guidelines
* You need to be confident in what you are translating. You should be able to read the text you just translated and understand the meaning of it very easily.
* It should use the simplest form of language possible. Use English words if the translation is unknown to the majority in your country.
* Use your imagination, trust yourself. Feel free to change any context as you wish as long as it can still get the same point across and is understandable.
* These guidelines are more important than language-specific guidelines, but less important than Hytale terminology and rules.
## What not to translate
We request you all to not translate these specific sections of the website:
### Callout types
Callouts are the boxes that contain important information. We request you to not translate the types as they only work in English.
**Example:**
```mdx
Translate the text inside the callout!
```
In this example, please don't translate the word warning, the rest you can translate.
### Icons
Icon names must not be translated.
They are technical identifiers mapped directly to existing icons. Translating them will break the icon rendering.
```mdx
icon: Globe
```
***
{/*
For translators who will edit this page:
- You are required to translate the Guidelines as they change.
- General guidelines should be translated but not edited.
- Learn how to use Markdown from [Writing Guides](.contributing/writing_guides)
*/}
--- Contributing
Source: https://hytalemodding.dev/en/docs/contributing
# Contributing to HytaleModding
We welcome contributions from the community to help improve and expand the HytaleModding documentation. Whether you're fixing typos, adding new tutorials, or providing code examples, your contributions are valuable!
## How to Contribute
### Reporting Issues
If you've found a issue with the documentation, please report it by opening a new issue on our [GitHub repository](https://github.com/HytaleModding/site/issues).
If you can work on this issue yourself, you can ask to be assigned to it and submit a PR.
### Submitting Pull Requests
1. Fork the repository on GitHub.
2. Clone your fork to your local machine.
3. Create a new branch for your changes.
```bash
git checkout -b my-feature-branch
```
4. Make your changes and commit them with clear messages. We follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), your PR will not be accepted if your commit messages do not follow this convention.
```bash
git commit -m "feat: add feature X to documentation"
```
5. Push your changes to your fork.
```bash
git push origin my-feature-branch
```
6. Open a Pull Request on the original repository, describing your changes and why they should be merged.
--- Learning to Learn
Source: https://hytalemodding.dev/en/docs/guides/learning-to-learn
## Why learn to code? We have visual scripting!
Yes we do, and honestly you should try that out. Visual scripting is a great addition to Hytale and we know it'll be powerful, but nothing beats the ability of being able to read and write Java code, with the ability to build even more powerful mods and plugins.
Visual scripting is great for beginners and for simple tasks, but coding allows for more complex and efficient solutions. Learning to code opens up a wider range of possibilities for modding Hytale.
We will have guides on visual scripting when Hytale releases from Day 1.
## Should everyone trying to mod Hytale learn how to code?
No one should feel like they HAVE to learn to code to make stuff in Hytale. Simon and his team have validated this time and time again with their strives in graph editors and visual scripting. You can change mob behavior, world generation, etc. all with just these tools.
But if you're looking to build more complex things (from utilities to entirely custom server software) that could be used by large scale server networks and big names, you'll want to learn how to code. And that's where this guide comes in...
## Okay, I'm interested. What's to come?
Over the course of these next several lessons you'll learn the basics of coding, how to read documentation, and how to find resources to help you learn more on your own.
The guides themselves will take a very big "bird's-eye view" approach to things and provide links to resources to do the actual coding. This is done because, quite frankly, enough resources exist on how to learn this stuff, it's just the act of finding the right stuff on Google that can be hard. Learning to be able to code without guidance is the goal. The Hytale Modding community is always there to help you, but to become a truly well rounded developer, you'll have to pick up a lot of this stuff just by reading from resources.
We have created a small Java Basics series to help you get started with the language itself, but beyond that, you'll be expected to find your own resources to learn more. We recommend sites like [w3schools](https://www.w3schools.com/java/) and [GeeksforGeeks](https://www.geeksforgeeks.org/java/) as great starting points.
Our goal is to teach you how to Mod Hytale, and that will always be our goal. This is the reason we can't teach you everything about Java or programming in general. Instead, we want to give you the tools to be able to learn on your own, so you can continue to grow as a developer even after you've completed our guides.
## Any closing thoughts?
Remember, learning to code is hard. You will trip and fall, but you will be able to get back up. Stay resilient, believe you can make cool stuff, and I promise you success will come. Happy coding!
--- Prefabs
Source: https://hytalemodding.dev/en/docs/guides/prefabs
# Introduction
Prefabs are Hytale's way of creating reusable structures.
There are a few fundamental concepts to understand about prefabs:
* Users interact with prefabs via a prefab editing world.
* Prefabs are the physical structures in the game world, and are saved as JSON files.
## How to Create a Prefab
Be sure to check the Known Issues section at the end of this guide if you encounter any problems.
1. `/editprefab new ` to create a new prefab editing world: `/editprefab new my_prefab_world`
* Note that `my_prefab` is the name of the *world*, not the prefab itself. Worlds can contain multiple prefabs.
2. Build whatever you want in the prefab editing world.
3. Use the selection brush to select the area you want to save as a prefab.
4. `/prefab save` to save the structure as a prefab.
5. `/editprefab exit` to exit the prefab editing world.
6. Using the Paste brush, press 'e' on your keyboard to select the prefab you just created. It will be in the "server" dropdown, which can be selected from the top right of the menu.
* You can also select it with `/prefab list` and selecting the prefab you just created.
There is a lot more that you can do with prefabs; this is just the basic flow. Keep reading for more information.
## Commands
There are two main commands for prefabs, `/prefab` and `/editprefab`.
This list does not cover the various options for each command. You can use the `--help` flag at the end of any command to see the available options, e.g. `/prefab save --help`, or use `/help` in game and find the `prefab` and `editprefab` commands.
### `/prefab`
This command allows the user to interact with an existing prefab's file.
Subcommands:
* `save`
* Saves the prefab to the file system.
* `load`
* Loads the prefab into the game.
* `delete`
* Deletes the prefab from the file system.
* `list`
* Lists all prefabs in the file system.
### `/editprefab`
The `/editprefab` command is used to create or edit the physical structure in the game the prefab is holding.
Subcommands:
* `exit`
* Exits the current prefab editing world.
* `load`
* Creates a new prefab editing world to paste an existing prefab into.
* `new`
* Creates a new prefab editing world to create a new prefab from scratch.
* `select`
* Selects the area of the prefab the user is looking at (within 200 blocks).
* `save`
* Saves the current prefab using the existing area, or the currently selected area.
* `saveui`
* Opens the save UI for the current world, allowing the user to interact with all prefabs that are in the world.
* `kill`
* Despawn all entities in the currently selected prefab.
* `saveas`
* Save the selected prefab into a new file.
* `setbox`
* Set the bounding box of the currently selected prefab.
* `info`
* General information about the currently selected prefab.
* `tp`
* Opens the teleport UI to teleport to a prefab in the current editing world.
* `modified`
* Lists all modified prefabs with unsaved changes in the current editing world.
# Known Issues
* After saving edits to an existing prefab, the prefab does not reflect the changes in the game.
* Workaround: exit and re-enter the world.
* When pasting a prefab, the prefab is not always shown accurately.
* Workaround: hit 't' on your keyboard to toggle the material being shown; usually at least one view is accurate.
* `/prefab delete` errors with `Assert not in thread`.
--- Modtale
Source: https://hytalemodding.dev/en/docs/publishing/modtale
# Publishing Your Mod on Modtale
[Modtale](https://modtale.net) is the Hytale Community Repository for discovering mods, asset packs, worlds, and modpacks.
This guide will walk you through the complete process of publishing your mod.
## Prerequisites
Before you start, make sure you have:
* A compiled `.jar` file of your plugin, or `.zip` of your asset pack
* A description of your mod's features
We also recommended:
* High-quality screenshots or images of your mod in action
* A clear logo or icon (recommended size: 512x512px)
* Any dependencies or requirements documented
## Step-by-Step Publishing Process
### 1. Account Setup
Sign in to [Modtale](https://modtale.net) using your preferred method. If you don't have an account, create one first.
### 2. Start Creating
Click on the **"Create"** button in the navigation bar or go directly to the [upload page](https://modtale.net/upload).
### 3. Choose Content Type
Select the appropriate content type for your submission:
* **Server Plugins** - For Hytale server-side plugins and modifications
* **Data Assets** - For custom data packs, loot tables, and other data-driven content
* **Art Assets** - For texture packs, models, and other artistic content
* **Worlds** - For custom Hytale worlds
* **Modpacks** - For curated collections of mods and assets
### 4. Basic Information
Fill in the essential details:
* **Name**: Choose a clear, descriptive name for your mod
* **Summary**: Write a brief description (1-2 sentences, 250 char max) that explains what your mod does
### 5. Project Configuration
Now you're on your project's management page. Here you can:
#### Upload Files
Navigate to the **"Files"** tab and upload your `.jar` or `.zip` file:
* Drag and drop your file or use the upload button
* Add version notes describing changes and new features
* Mark release type (Release, Beta, Alpha)
#### Customize Your Project
* **Logo**: Upload a high-quality logo (512x512 recommended)
* **Banner**: Upload a banner for your page (1920x640 recommended)
* **Screenshots**: Add multiple screenshots showing your mod in action
* **Description**: Write a comprehensive description using Markdown formatting
* **Links**: Add links to your source code, Discord server, or documentation
* **Tags**: Add relevant tags to help users find your mod
* **License**: Indicate what license your mod uses from a selection of common licenses, or use a custom license
## Best Practices
### Documentation
Always include comprehensive documentation in your mod description. Users should understand what your mod does, how to use it, and any special requirements.
### Version Notes
When uploading new versions:
* You must adhere to SemVer
* Clearly document all changes and new features
* Include bug fixes and known issues
* Mention any breaking changes or migration steps
* List new dependencies or requirement changes
### Visual Appeal
* Use high-quality screenshots that showcase your mod's features in the gallery
* Create an eye-catching logo that represents your mod
* Consider adding a banner image for your project page
### Community Engagement
* Respond to comments and feedback promptly
* Consider creating a Discord server for community support
* Regularly update your mod based on user feedback
## After Publishing
### Automated updates
* You can configure your project to automatically upload new versions using Github Actions, Gradle, Maven, or Go. See examples [here](https://github.com/Modtale/modtale-example)
### Monitoring Your Project
* Check the analytics to see download statistics
* Monitor comments and reviews
* Keep track of bug reports and feature requests
### Marketing Your Mod
* Share your mod on relevant Hytale communities
* Create showcase videos or tutorials
* Engage with other modders and creators
Remember to keep your mod updated for new Hytale versions and maintain compatibility with Hytale's modding framework to maximize your audience reach.
## Getting Help
If you encounter any issues during the publishing process:
* Join their Discord community for support
* Contact the Modtale team directly through their support channels
--- Modifold
Source: https://hytalemodding.dev/en/docs/publishing/modifold
# Publishing Your Mod on Modifold
[Modifold](https://modifold.com) is a modding platform where modders can upload their mods and share them with the community.
This guide follows the real project publishing flow: from signing in to your account to submitting for moderation.
## Step 1. Sign In
First, you need to sign in.\
In the site header, click **"Log in"** and choose your preferred sign-in method.
Currently available:
* GitHub
* Discord
* Telegram
## Step 2. Create a Project
After signing in successfully, click **"Create Project"** in the site header.
Fill in:
* **Project name**
* **Summary** (a short description in 1-2 sentences, for example: `This project adds...`)
Then click **"Create Project"**.
After creation, you will immediately be taken to the project settings page.
## Step 3. Project Icon
It is recommended to upload a project icon first.
It is best to use an in-game preview of your mod so users can immediately understand what the project does.
Uploading an icon is not required, but highly recommended.
## Step 4. Description
Make sure to fill in the project's **Description**.
[Modifold](https://modifold.com) supports a convenient Markdown editor where you can describe in detail:
* what your mod does
* how to use it
* important details for users
Banners linking to other platforms are allowed.\
Minimum description length: **50 characters**.
## Step 5. Upload a Mod Version
This is the most important step.
Upload your project version file (for example, `.jar`) through the upload block:
* drag and drop the file, or
* click the block and select a file from your computer
Then fill in:
* **Version number** (for example, `1.0.1` if this is a fix)
* **Changelog** (optional, in Markdown format)
* **Release channel**: `Release`, `Beta`, or `Alpha`
After that, click **"Upload Version"**.
Congratulations, your first version is uploaded.
If you publish on Modifold, keep your project page updated so users can quickly understand what your mod does and which version they should install.
## Step 6. Gallery
In the **"Gallery"** tab (recommended), you can upload images showcasing your mod's features.
One image can be marked as **Featured**.
The featured gallery image appears in search results and on your project card.
This image will be used as your project's cover.
## Step 7. Links
In the **"Links"** tab, you can add useful project links:
* Issue Tracker
* Source Code
* Wiki (linking to external resources)
* HytaleModding Wiki
* Discord
These links are shown in a separate block on the project page.
## Step 8. Tags
In the **"Tags"** tab (recommended), select relevant project tags.\
For example, if your mod adds decorative elements, choose the `Decoration` tag, then click **"Save"**.
## Step 9. License
In the **"License"** tab, you can set the project license.
By default, the license is **ARR (All Rights Reserved)**, but you can choose another available license.
## Step 10. Moderation
The final required step is the **"Moderation"** tab.
There, you can make sure all required fields are completed and submit the project for review.
After submission, moderators check:
* compliance with Modifold content rules
* project versions for malware
When the review is complete, you will receive a notification:
* if the project is approved, it becomes public
* if it is rejected, moderators will provide a reason, and you can resubmit after making fixes
# Integration with HytaleModding Wiki
If your mod uses [HytaleModding Wiki](https://wiki.hytalemodding.dev), just add the wiki link in your Modifold project settings.
After that, your project will have a **Wiki** tab with synchronized pages and Markdown content.
Wiki configuration and editing remain on the [HytaleModding Wiki](https://wiki.hytalemodding.dev) website.
The screenshot above shows the Voile project wiki page: [modifold.com/mod/voile/wiki](https://modifold.com/mod/voile/wiki)
# Getting Help
If you run into issues while uploading your project, please:
* Contact the official Modifold Discord server
* Or email `support@modifold.com` with a description of the issue.
--- BuiltByBit
Source: https://hytalemodding.dev/en/docs/publishing/builtbybit
# Why BuiltByBit?
BuiltByBit is the largest third-party marketplace for Minecraft and Roblox, and now Hytale. Unlike other platforms, BuiltByBit is primarily focused on premium content, allowing creators to monetize their work (though free resources are also welcome)!
Within just days of Hytale's early access launch, creators on BuiltByBit have already earned thousands of dollars selling their Hytale creations.
Key advantages:
* Monetize your work - Set your own prices for premium content with no maximum limit
* Fast approval times - Industry-leading review times averaging just 4 hours
* More categories - The widest range of content types of any Hytale platform
* Established marketplace - Join a proven ecosystem with hundreds of thousands of active buyers
# Available Categories
BuiltByBit supports the following categories:
Plugins - Server-side plugins and modifications
Data assets - Custom data packs, loot tables, and data-driven content
Server setups - Complete server configurations and setups
Builds - Prefabs, structures, and world builds
Graphics - UI elements, icons, and visual assets
Textures - Texture packs and reskins
Models - Custom 3D models and animations
Audio - Sound effects and music
Other - Anything else that doesn't fit the above
# Prerequisites
Before you start, make sure you have:
* A compiled `.jar` file (for plugins) or `.zip` file (for other content)
* A description of your creation's features
* A Tebex account for receiving payments (can be created during setup)
We also recommend:
* High-quality screenshots showcasing your creation
* A clear logo or icon
* Documentation of any dependencies or requirements
# Step-by-Step Publishing Process
## 1. Create Your Account
Register for a BuiltByBit account at [builtbybit.com/login/register](https://builtbybit.com/login/register).
## 2. Set Up Your Wallet
Link your Tebex Wallet (or create one if you don't have one) at [builtbybit.com/resources/dashboard/wallet](https://builtbybit.com/resources/dashboard/wallet). This is where your earnings will be deposited.
## 3. Add Your Resource
Navigate to [builtbybit.com/resources/add](https://builtbybit.com/resources/add) and select the appropriate category for your content.
## 4. Complete Your Listing
Fill out all required fields:
* Title - A clear, descriptive name for your creation
* Description - Comprehensive details about features and usage
* Tags - Relevant keywords to help buyers find your content
* Pricing - Choose free or set a price (minimum $1.99 to $5.99 for paid content depending on category)
* File Upload - Upload your .jar or .zip file
## 5. Submit for Approval
Click "Submit for approval" to send your resource to our moderation team.
## 6. You're Done!
Once approved, your resource will be live and accessible to buyers. With our review times averaging just 4 hours, you won't be waiting long.
# Pricing & Fees
* Minimum price: $1.99 to $5.99 for paid content depending on category (no maximum)
* Platform fee: 9.9% per transaction
* Gateway fee: Reduced rate charged per transaction
The 9.9% platform fee allows us to continuously introduce new features and improvements. [Learn more about fees](https://builtbybit.com/resources/creators).
# Getting Help
* Creator Knowledge Base - [creators.builtbybit.com](https://creators.builtbybit.com) - Articles covering every feature
* Discord Community - [discord.gg/builtbybit](https://discord.gg/builtbybit) - Dedicated channels for Hytale creators (sync your account to access)
* Become a Creator - [builtbybit.com/resources/creators](https://builtbybit.com/resources/creators) - Overview of selling on BuiltByBit
* Browse Hytale Resources - [builtbybit.com/resources/hytale](https://builtbybit.com/resources/hytale) - See what other creators are selling
--- Publishing Your Mod
Source: https://hytalemodding.dev/en/docs/publishing
Now that you've created your mod, it's time to share it with the world! There are many marketplaces that allow you to publish your mod, such as Modtale, CurseForge, BuiltByBit, etc.
We have compiled guides to publish for each marketplace, so you can easily get your mod out there and into the hands of players, no matter the marketplace they use.
* [Publishing on Modtale](./publishing/modtale)
* [Publishing on CurseForge](./publishing/curseforge)
* [Publishing on BuiltByBit](./publishing/builtbybit)
* [Publishing on Modifold](./publishing/modifold)
* [Publishing on Thunderstore](./publishing/thunderstore)
If you run a marketplace, please reach out to us via our email `neil@hytalemodding.dev` to get your marketplace listed in our documentation.
--- CurseForge
Source: https://hytalemodding.dev/en/docs/publishing/curseforge
CurseForge is a mod hosting platform, allowing you to share your Hytale mods and creations with players via our website and app.
## 1. Create CurseForge Account
First, [visit our sign-in page](https://curseforge.com/api/v1/auth/login?returnUrl=https://www.curseforge.com/) and either log in to an existing account or click “Create an account”.
This can be done using any Google, Discord, GitHub or Twitch account.
## 2. Create a Project
Now that you're logged in, you can visit our Author Console and navigate to the Projects tab, then click “Create a Project”, either in the sidebar or at the top of the page.
This will then take you to the [project creation](https://authors.curseforge.com/#/projects/create/choose-game) flow. First, select your game (in this case, Hytale) for and the page will be updated with new fields waiting for your inputs. Please see our
[Moderation Policies](https://support.curseforge.com/en/support/solutions/articles/9000197279-moderation-policies#:~:text=General%20CurseForge%20Moderation%20Policies,-Policy\&text=The%20description%20can%20include%20descriptive,the%20game%22%20is%20not%20enough) articles for more information about what is and isn't allowed when creating a new project. Below are the fields along with their descriptions:
* **Name:** This is the title of your project. It should be unique to your project. If the name is already taken, it will be rejected.
* **Summary:** This is a short blurb about the mod that will show up in the project listings
* **Description:** This is the longer explanation of what your mod does/adds. Make sure you clearly elaborate on your project. We will reject your project if the description is not grammatically correct or if it does not sufficiently describe the project you are creating. This can be edited later. Note: All titles and descriptions must be in English.
* **Project License:** This is a drop-down field that contains the most common licenses used in modding games. Research the license type you want to use, including its pros and cons. You can also choose to use a Custom License at the bottom of the dropdown list and enter any text you want.
* **Class:** This is the root category for your project. Ensure you select the correct one, as it may impact supported file types and app support in CurseForge.
* For Hytale, there are four types of Class:
* Mods
* This is the class that contains both Asset Packs and Plugins, and you should upload either here.
* Worlds
* Prefabs
* Bootstrap
* This is the class that contains all Early Plugins.
* **Main Category:** This is the primary category into which your project will be sorted.
* **Additional categories:** You can add up to 4 of these. Your project will also show up in lists where people are searching for these types of mods. Be careful, though, if you just add categories that have nothing to do with your mod, we may send it back to you for changes.
* **Logo Image:** A unique icon to identify your project by.
## 3. Upload a File
Once you've set up your project and filled in the above information, you now need to upload a file to your project for it to enter the moderation queue. To do this, visit the “Files” tab in your Author Console page.
Then, click on “Add File” and upload your mod's file, entering any additional information or meta you wish to include regarding this file.
The file format will depend on the class you select.
### For Plugins
If you're uploading a plugin for Hytale, please make sure you select the "Mods" class and upload your file as a jar file.
### For Packs
If you're uploading a Pack for Hytale, please make sure you select the "Mods" class and upload your file as a ZIP file
### For Worlds
If you're uploading a World for Hytale, make sure you upload it to the "Worlds" class and upload your file as a ZIP file.
**Once you've uploaded your file, it will be sent for our moderation team to review, and once approved, it will be live for all players on CurseForge!**
## 4. Additional Resources & Guides
You can refer to the following resources for guides on how to create a project page, using the upload API, and creating the best project page:
* How to Pass Moderation Review: [https://blog.curseforge.com/how-to-pass-moderation-review-on-curseforge-2/](https://blog.curseforge.com/how-to-pass-moderation-review-on-curseforge-2/)
* Creating and Submitting a Project: [https://support.curseforge.com/en/support/solutions/articles/9000197241-creating-and-submitting-a-project](https://support.curseforge.com/en/support/solutions/articles/9000197241-creating-and-submitting-a-project)
* How to Create the Best Project Page: [https://blog.curseforge.com/how-to-create-the-best-project-page/](https://blog.curseforge.com/how-to-create-the-best-project-page/)
* Our Mod Approval Process: [https://blog.curseforge.com/our-mod-approval-process/](https://blog.curseforge.com/our-mod-approval-process/)
--- 5.1 - Markdown
Source: https://hytalemodding.dev/en/docs/wiki/5-styling/5-1-markdown
## Text formatting
```md
**Bold text**
*Italic text*
~~Strikethrough~~
**_Bold and italic_**
```
## Headings
```md
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
```
## Lists
### Unordered
```md
- Item
- Item
- Nested item
- Nested item
- Item
```
### Ordered
```md
1. First
2. Second
1. Nested
2. Nested
3. Third
```
### Task list
```md
- [x] Done
- [ ] Not done
```
## Links
```md
[Link text](https://example.com)
[Link with title](https://example.com "Hover title")
```
## Images
The site may contain images from external resources.
```md


```
## Code
### Inline
```md
Use `code` inside text.
```
### Block
````md
```js
const hello = "world";
console.log(hello);
\```
````
Supported language hints: `js`, `ts`, `css`, `html`, `json`, `md`, `bash`, and others.
## Blockquotes
```md
> This is a blockquote.
> It can span multiple lines.
>
> And multiple paragraphs.
```
## Alerts
Alerts are styled blockquotes for highlighting important information.
```md
> [!NOTE]
> Useful information the reader should know.
> [!TIP]
> Helpful advice for doing things better.
> [!IMPORTANT]
> Key information the reader must be aware of.
> [!WARNING]
> Potential issues the reader should watch out for.
> [!CAUTION]
> Actions that may cause problems or data loss.
```
## Tables
```md
| Column A | Column B | Column C |
| -------- | -------- | -------- |
| Cell | Cell | Cell |
| Cell | Cell | Cell |
```
Alignment:
```md
| Left | Center | Right |
| :------- | :------: | -------: |
| Cell | Cell | Cell |
```
## Horizontal rule
```md
---
```
## Footnotes
```md
Some text with a footnote.[^1]
[^1]: This is the footnote content.
```
## Escaping characters
```md
\*Not italic\*
\# Not a heading
```
Imported components are only available when editing via GitHub sync.
The built-in editor supports custom components registered on the platform.
--- 5.3 - Custom CSS
Source: https://hytalemodding.dev/en/docs/wiki/5-styling/5-3-custom-css
The wiki supports custom CSS, letting you change colors, typography, spacing, and more.
To make the most of it, a basic understanding of CSS is recommended —
if you're new to it, [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/CSS) or [W3Schools](https://www.w3schools.com/css/default.asp) are great places to start.
To add custom CSS, go to your mod page and find the **Custom CSS** field.
## CSS variables
The platform is built with [shadcn/ui](https://ui.shadcn.com) and [Tailwind CSS](https://tailwindcss.com).
The most effective way to style your wiki is through CSS variables on `:root` —
the platform exposes a set of them that control colors, borders, and states across the entire wiki.
Overriding them affects every page of your mod at once:
```css
:root {
--primary: #e63946;
--background: #0d0d0d;
}
```
### Variables reference
```css
:root {
/* Page */
--background: ; /* Page background */
--foreground: ; /* Main text color */
--border: ; /* Element borders */
--radius: ; /* Border radius */
/* Cards */
--card: ; /* Card background */
--card-foreground: ; /* Card text color */
/* Primary accent */
--primary: ; /* Accent color — icons, mod title */
--primary-foreground: ; /* Text on primary-colored elements */
/* Secondary */
--secondary: ; /* Secondary background */
--secondary-foreground: ; /* Secondary text */
/* Muted */
--muted: ; /* Subtle background (badges, tags) */
--muted-foreground: ; /* Subtle text (descriptions, timestamps) */
/* Hover / active states */
--accent: ; /* Hover background */
--accent-foreground: ; /* Hover text */
/* Danger */
--destructive: ; /* Delete buttons, error states */
}
```
### Direct selectors
For things not covered by variables, you can target elements directly.
By default, `--radius` is not applied to cards — but you can connect it manually:
```css
[data-slot="card"] {
border-radius: var(--radius);
}
```
## Prose
To style the content of your pages, use `.prose` combined with the element selector.
For example, to make all `h1` headings red:
```css
.prose h1 {
color: red;
}
```
### Selectors reference
A full list of available selectors, matching the elements from [Markdown](./5-1-markdown):
````css
/* Headings */
.prose h1 { } /* # Heading 1 */
.prose h2 { } /* ## Heading 2 */
.prose h3 { } /* ### Heading 3 */
.prose h4 { } /* #### Heading 4 */
/* Text */
.prose p { } /* Paragraph */
.prose strong { } /* **Bold** */
.prose em { } /* *Italic* */
.prose del { } /* ~~Strikethrough~~ */
/* Links */
.prose a { } /* [Link](url) */
/* Lists */
.prose ul { } /* - Unordered list */
.prose ol { } /* 1. Ordered list */
.prose li { } /* List item */
.prose input[type="checkbox"] { } /* - [x] Task list checkbox */
/* Images */
.prose img { } /*  */
/* Code */
.prose code { } /* `Inline code` */
.prose pre { } /* ```Code block``` */
.prose pre code { } /* Code inside block */
/* Blockquote */
.prose blockquote { } /* > Blockquote */
/* Alerts */
.prose .markdown-callout { } /* > [!NOTE] wrapper */
.prose .markdown-callout-note { } /* > [!NOTE] */
.prose .markdown-callout-tip { } /* > [!TIP] */
.prose .markdown-callout-important { } /* > [!IMPORTANT] */
.prose .markdown-callout-warning { } /* > [!WARNING] */
.prose .markdown-callout-caution { } /* > [!CAUTION] */
.prose .markdown-callout-title { } /* Alert title */
.prose .markdown-callout-content { } /* Alert body */
/* Table */
.prose table { } /* | Table | */
.prose thead { } /* Header row */
.prose th { } /* Header cell */
.prose td { } /* Data cell */
.prose tr { } /* Row */
/* Horizontal rule */
.prose hr { } /* --- */
/* Footnotes */
.prose sup { } /* Footnote reference [^1] */
.prose .footnotes { } /* Footnote block */
````
## Custom classes
To style a specific part of a page, create a custom class in CSS and apply it as a `div` in MDX.
For example, a `.green` class with green text:
```css
.prose .green {
color: green;
}
```
```mdx
Green text!
```
***
Now you know how to style your wiki with custom CSS.
--- 5.2 - Images
Source: https://hytalemodding.dev/en/docs/wiki/5-styling/5-2-images
## Uploading images
To upload images, go to your mod's management page and click **Upload Files**.
Once uploaded, copy the file link and use it in your page:
```md

```
You can also add a title that appears on hover:
```md

```
--- 5 - Styling
Source: https://hytalemodding.dev/en/docs/wiki/5-styling
## Overview
The wiki supports Markdown for text formatting, along with custom extensions for more advanced visuals.
In this section you will find:
* [**Markdown**](./markdown): Headings, lists, tables, code blocks, alerts, and more.
* [**Images**](./images): How to insert and configure images.
* [**Custom CSS**](./custom-css): Apply your own styles to make your wiki stand out.
--- 7 - Sleep Animations
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/7-sleep-animation
There are still a few more things we need to do before our ogre can get some rest. Until we come to inter-NPC behaviours later, most of this is purely for visual effect and is pretty much entirely restricted to playing animations.
The most basic part is playing the sleep animation. We can use a convenient component for this. Don't forget to put back **"Continue":true**, because now we want to play the animation while the timeout runs.
```json
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [
{
"Continue": true,
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [5, 10]
},
{
"Type": "State",
"State": "Idle"
}
]
},
{
"Reference": "Component_Instruction_Play_Animation",
"Modify": {
"Animation": "Sleep"
}
}
]
}
```
Wait...that's it?
Almost! While our ogre will now happily go to sleep, it's a bit janky. We still need to handle the transitions between states. For this we add a completely new section to the asset above the instructions, called **StateTransitions**.
```json
"StateTransitions": [
{
"States": [
{
"From": ["Idle"],
"To": ["Sleep"]
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Laydown"
},
{
"Type": "Timeout",
"Delay": [1, 1]
}
]
},
{
"States": [
{
"From": ["Sleep"],
"To": []
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Wake"
},
{
"Type": "Timeout",
"Delay": [1, 1]
}
]
}
],
"Instructions": [
...
]
```
State transitions are a bit verbose but relatively simple. Each transition represents a set of actions that will be performed in sequence before we actually start executing the logic of the state itself.
An empty array represents all existing states. In this instance, our ogre will play the **Laydown** animation when switching from **Idle** to **Sleep** and the **Wake** animation when switching from **Sleep** to any other state.
We currently have to define a **Timeout** action with a delay the length of the animation to ensure that we don't do anything else until the animation completes. In the future, we might be able to automatically find out the length of the animation and block for its duration, but that's not currently possible.
But that's all for the **Sleep** state for now. Really this time.
--- 1 - Know Your Enemy
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/1-know-your-enemy
Constructing NPCs largely always follows the same set of initial steps:
1. Read through any design documentation.
2. Figure out how the behaviour breaks down into distinct states.
3. Identify which parts of the behaviour already exist in predefined components.
4. Identify any parts of the behaviour that are likely to be reused across other similar templates.
5. (Optional) Identify any particularly complex parts of the behaviour that are likely to become generally reusable.
For this tutorial, we're looking at the **Goblin Ogre**.
**Minimum design requirements we were given:**
1. A big and slow NPC with a lot of health, found in specific POIs.
2. Can follow a pre-determined path and guard that position.
3. Can sleep and if other goblins annoy the Ogre, there will be a special "sleepy" reaction to that.
4. Can summon rats to eat.
5. Has a melee attack
**Note:** For the purposes of this tutorial, we'll stick to this design brief, though it may no longer reflect the NPC used in-game.
***
### Step 1: Read the documentation!
This is also a strong suggestion to take a look through the NPC core element documentation and component documentation [here](../npc-doc), if you haven't already. It provides an overview of all core elements available as sensors/actions/motions/etc. Refer to it anytime you want to know if something is possible and what's needed to achieve it (e.g. picking up items). This will also be updated over time as more features are added!
### Step 2: Decide on the states!
The Goblin Ogre isn't too complicated in this regard. We start with the main top-level states:
* An **Idle** state where it mostly remains stationary at a specific point.
* This can also encompass its general 'observed' flavour actions.
* In many cases, we can include some inter-NPC behaviours here too, unless they require a more complex sequence of actions.
* A **Sleep** state.
* Sleep states are often best kept separate from the idle state, both to take advantage of mechanisms for handling the transition from the state to others (e.g. playing wake up animations) and to accommodate slight changes to the NPC's detection capabilities.
* An **Eat** state.
* With the same reasoning as Sleep.
* An **Alerted** state.
* Almost all NPCs end up having one of these in some form, though they often vary a bit in their intent.
* A **Combat** state.
* While not always the case, it often makes sense to roll all combat into a single distinct state.
* A **ReturnHome** state.
* This applies particularly to NPCs that have stationary guard points.
* Essentially handles getting them home again.
Some of these might break down into a set of additional substates handling individual parts of the behavioural logic. For example,
* **Idle**
* **.Default** (stand guard and do nothing else)
* **.FindFood** (go search for some nearby food if it exists)
* **.EatRat** (murder an innocent nearby rat)
Now we have a rough idea of how this NPC is going to be structured at the highest level. With this in mind, we can also see some potential for **state transitions** that make sense.
* **Any State -> Sleep**
* Play a laydown animation
* **Sleep -> Any State**
* Play a get up animation
* **Any State -> Eat**
* Prepare items for eating
* **Eat -> Any State**
* Pull out weapons again
These particular state transitions are pretty generic and commonly used, but at this stage, I don't see the need for any others.
### Step 3: Find existing components we can reuse so we can save ourselves some work!
Straight away, there are a few pre-existing components that will make our lives significantly easier.
* We need to chase and attack other targets.
* `Component_Instruction_Intelligent_Chase` is built for exactly this purpose and abstracts away a lot of complicated logic needed to make an NPC smartly try to track down a target based on its last seen position.
* `Component_Sensor_Standard_Detection` will help setting up NPC senses, like vision and hearing.
* `Component_Instruction_Soft_Leash` will make sure the ogre doesn't chase its target to the far reaches of Orbis and will eventually give up if the player is just running away.
These will handle a chunk of our generic combat logic. We also need to add supporting files, like an **Appearance** file that describes what model and animations are to be used and an **Attack Interaction** for combat.
The ogre does more than just attack though! Unfortunately, the bulk of the other behaviour is quite specific to the ogre itself. There are existing components for handling sleep states, but this creature needs to be able to swat at other goblins in its sleep so we can't make use of them. The general idle components aren't much use either, but we might be able to handle the simple standing guard by using `Component_Instruction_Intelligent_Idle_Motion_Follow_Path`. There are also some general utility components that might prove useful as we build up the template, such as `Component_Instruction_Damage_Check`.
### Step 4 & 5: Identify parts we can reuse in other goblins (or are generally useful to have)
There is a small chance that eating rats and searching for food might be reusable for other goblins, but owing to the uncertainty here, it makes sense to build the NPC without worrying too much about it and then extract the logic out into a component later if required.
--- 10 - Attacking a Player
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/10-attack-a-player
There are actually two parts to dealing with the player in this design description and the resulting state machine. We have the initial **Alerted** state, followed by combat itself. For now, we'll start with implementing all the awareness checks and the **Alerted** state, which acts as a transitional state to the combat behaviours themselves.
First we'll just create the empty **Alerted** state after the **CallRat** state with a timeout to send it back to **Idle**. The template will stop compiling until we add references to it via the awareness checks, but that's fine.
```json
{
"Sensor": {
"Type": "State",
"State": "Alerted"
},
"Instructions": [
{
"Reference": "Component_Instruction_State_Timeout",
"Modify": {
"_ExportStates": ["Idle"],
"Delay": [10, 15]
}
}
]
}
```
There are a number of good components for handling awareness checks. For this we are going to use a handy Sensor component: **Component\_Sensor\_Standard\_Detection**. To make this work, we'll set up an attitude group (**Goblin**) that will likely be shared by all other goblins too and references a pre-existing **Goblin NPC group**. We can detect different NPCs depending on the attitude and react accordingly.
```json
{
"Groups": {
"Friendly": ["Goblin"],
"Hostile": []
}
}
```
We don't actually have much in it for now since there aren't many other related NPCs implemented yet, but when there are, we can add any hostiles as hostile. We'll reference this in the parameters and assign it to the attitude group for the ogre, as well as adding a default attitude towards the player (and ignoring most NPCs by default). I'm not really expecting 'friendly' ogres, so I won't bother making the default player attitude a parameter for now.
```json
"Parameters": {
...
"AttitudeGroup": {
"Value": "Empty",
"Description": "This NPCs attitude group"
},
...
},
...
"DefaultPlayerAttitude": "Hostile",
"DefaultNPCAttitude": "Ignore",
"AttitudeGroup": { "Compute": "AttitudeGroup" }
```
In order to configure 'sight' and 'hearing' on the ogre, we also need to add a few more parameters relating to this.
```json
"Parameters": {
...
"ViewRange": {
"Value": 15,
"Description": "View range in blocks"
},
"ViewSector": {
"Value": 180,
"Description": "View sector in degrees"
},
"HearingRange": {
"Value": 8,
"Description": "Hearing range in blocks"
},
"AlertedRange": {
"Value": 30,
"Description": "A range within which the player can be seen/sensed when the NPC is alerted to their presence"
},
"AbsoluteDetectionRange": {
"Value": 4,
"Description": "The range at which a target is guaranteed to be detected. If zero, absolute detection will be disabled."
},
...
}
```
These parameters are pretty self explanatory, but the **ViewRange/ViewSector** handle how far the ogre can see and his view cone, **HearingRange** handles how far his hearing extends, and **AlertedRange** handles how far away the target can go while still being tracked after the ogre has been alerted to their presence. **AbsoluteDetectionRange** allows us to set a distance at which the Ogre will definitely react to us, regardless of any other conditions.
This Sensor has a few other parameters, for example we can explicitly exclude NPC groups from detection too! But for our purposes, just a simple Attitude filter will work.
Let's talk for a moment about how this works. The sensor has different checks:
* First it checks if there is something in **absolute detection range**, then if nothing satisfies the filter, it checks further.
* It then **checks if there is a potential target in ViewRange/Sector**. If there's a target within the view cone and range and there's an unobstructed line of sight to it, then we can 'see' it.
* If the NPC couldn't see anything, it will **try to 'listen'.** This is a little more specific - if a target is walking or running and isn't crouching, we can 'hear' it, regardless of most other factors (though it won't 'hear' through walls). This makes it pretty perceptive, which is why we tend to use it with a much lower detection radius than the view range.
```json
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Sensor": {
"Type": "State",
"State:" ".Default"
},
...
```
At this point, the template will compile again and we can observe in-game that approaching the ogre in various ways results in it switching to the **Alerted** state.
We want to add this component to the other idle states too, like sleeping and eating, but we probably want to reduce their detection capabilities a little bit in both cases. Let's add some kind of factor as a parameter.
```json
"Parameters": {
...
"DistractedPenalty": {
"Value": 2,
"Description": "A factor by which view range and hearing range will be divided when this NPC is distracted"
},
...
}
```
Then we'll use the previous configuration for the **CallRat** state, but a modified version for both the **Sleep** and **Eat** state. Only the **Sleep** state is shown in this next snippet but the others should follow suit as required. We usually place these checks immediately after any instructions that trigger only **Once** and **Continue** (these are basically initialisation instructions).
```json
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange / DistractedPenalty" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "ViewRange / DistractedPenalty" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
}
]
}
```
Here we can see a feature we haven't used before - the computed value actually encompasses a computation: we're dividing the **ViewRange** and **HearingRange** by the **DistractedPenalty** to result in a restricted detection radius.
Now, regardless of state, the ogre can react to any threats! Next we'll flesh out the **Alerted** state a bit so it actually does what's required of it. Let's recap what that was:
* Stand up if seated (this is covered by the state transitions already)
* Roar, ordering nearby goblin scrappers to attack
* Start slowly walking towards the player to attack (we'll consider this part of combat)
In that case, all we need to do here is make the ogre look at its target, play a roaring animation and maybe some particles, and send out a beacon to alert nearby goblin scrappers. First we need a quick **NPC group** (**Goblin\_Scrapper**) to define goblin scrappers so that we send our beacon to them specifically.
```json
{
"IncludeRoles": ["Goblin_Scrapper"]
}
```
We'll add a parameter referencing this too.
```json
"Parameters": {
...
"WarnGroups": {
"Value": ["Goblin_Scrapper"],
"Description": "The groups to warn when spotting an enemy"
}
...
}
```
And then implement the basic parts of the **Alerted** state, ending in a transition into a **Combat** state for combat.
```json
{
"Sensor": {
"Type": "State",
"State": "Alerted"
},
"Instructions": [
{
"Reference": "Component_Instruction_Play_Animation",
"Modify": {
"Animation": "Alerted"
}
},
{
"Continue": true,
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AlertedRange" },
"Filters": [
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
}
},
{
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AlertedRange" }
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [1, 1]
},
{
"Type": "State",
"State": "Combat"
}
]
},
{
"Actions": [
{
"Type": "State",
"State": "Idle"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Combat"
},
"Instructions": []
}
```
There's a fair bit of logic involved, but it's pretty straightforward. We play an animation, then we watch the target as long as there's line of sight to it. We then have a very short delay before switching to the **Combat** state. We'll handle actually sending the beacon message out to the other goblins using a state transition (and clear the animation at the same time).
```json
{
"States": [
{
"From": [],
"To": ["Combat"]
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status"
},
{
"Type": "Beacon",
"Message": "Goblin_Ogre_Warn",
"TargetGroups": { "Compute": "WarnGroups" },
"SendTargetSlot": "LockedTarget"
}
]
}
```
Before we move on to the actual combat behaviours in the **Combat** state, there's one more type of awareness check we need to implement: **damage**. It wouldn't do if a player could just hide away somewhere and snipe at our ogre without him reacting.
We can accomplish this very easily using the `Component_Instruction_Damage_Check` component, which is designed to assess if the NPC has received damage and respond accordingly. If the target is known and within a reasonable distance, it switches to the combat **Chase** state, otherwise it switches to a **Panic** state. Our ogre is pretty tough and has an important job, so we won't actually make him panic - if he takes damage, he's just going to switch to **Alerted** to warn others nearby and then run in and smash the threat!
Though only one instance is shown in the example, we place this in each of the places where we put the sight/sound checks, and we give it a higher priority by putting it first.
```json
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": ["Alerted", "Alerted"],
"AlertedRange": { "Compute": "AlertedRange" }
}
},
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
...
```
With that done, our ogre should respond to everything we need it to! Now we can focus on getting it to actually attack its target!
--- 2 - Getting started with Templates
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/2-getting-started-with-templates
There are no hard and fast rules for making a start on a template. I like to duplicate **BlankTemplate** and start from there because it already provides the basic structure of the file.
This leaves me with the following template (`Template_Goblin_Ogre.json`): (**note: the file could be looking different in the current version of the game**)
```json
{
"Type": "Abstract",
"Parameters": {
"Appearance": {
"Value": "Bear_Grizzly",
"Description": "Model to be used"
},
"DropList": {
"Value": "Empty",
"Description": "Drop Items"
},
"MaxHealth": {
"Value": 100,
"Description": "Max health for the NPC"
},
"NameTranslationKey": {
"Value": "server.npcRoles.Template.name",
"Description": "Translation key for NPC name display"
}
},
"Appearance": { "Compute": "Appearance" },
"DropList": { "Compute": "DropList" },
"MaxHealth": { "Compute": "MaxHealth" },
"MotionControllerList": [
{
"Type": "Walk",
"MaxWalkSpeed": 3,
"Gravity": 10,
"MaxFallSpeed": 8,
"Acceleration": 10
}
],
"Instructions": [
{
"Sensor": {
"Type": "Any"
},
"BodyMotion": {
"Type": "Nothing"
}
}
],
"NameTranslationKey": { "Compute": "NameTranslationKey" }
}
```
And the following variant (`Goblin_Ogre.json`) next to the template. There's a translation key in the template used for localisation of the NPC name, but we won't worry about that for now since it's not required for the NPC to work.
```json
{
"Type": "Variant",
"Reference": "Template_Goblin_Ogre",
"Modify": {
"Appearance": "Goblin",
"MaxHealth": 124
}
}
```
Our artists have not yet finished animations for the `Goblin_Ogre`, for that sake of the tutorial I am going to use appearance `Goblin`, because it offers a more complete set of animations that we will need.
--- 12 - Appendix
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/12-appendix
### Template\_Goblin\_Ogre\_Tutorial
(dependant on Root\_NPC\_Goblin\_Ogre\_Attack - you need this file to exist, because template is referencing it)
```json
{
"$Comment": "Debug: DisplayState",
"Debug": "DisplayState",
"Type": "Abstract",
"Parameters": {
"Appearance": {
"Value": "Bear_Grizzly",
"Description": "Model to be used"
},
"DropList": {
"Value": "Empty",
"Description": "Drop Items"
},
"MaxHealth": {
"Value": 100,
"Description": "Max health for the NPC"
},
"EatItem": {
"Value": "Food_Beef_Raw",
"Description": "The item this NPC will find when it rummages for food"
},
"SleepingAttack": {
"Value": "Root_NPC_Attack_Melee",
"Description": "Attack to use on NPCs that annoy it while sleeping"
},
"FoodNPCGroups": {
"Value": ["Edible_Rat"],
"Description": "The groups of edible NPCs that will come from triggering the beacon"
},
"FoodNPCBeacon": {
"Value": "Edible_Rat",
"Description": "The spawn beacon to trigger to create an edible NPC"
},
"FoodNPCItem": {
"Value": "Food_Cheese",
"Description": "The edible NPC in item form"
},
"ViewRange": {
"Value": 15,
"Description": "View range in blocks"
},
"ViewSector": {
"Value": 180,
"Description": "View sector in degrees"
},
"HearingRange": {
"Value": 8,
"Description": "Hearing range in blocks"
},
"AlertedRange": {
"Value": 30,
"Description": "A range within which the player can be seen/sensed when the NPC is alerted to their presence"
},
"DistractedPenalty": {
"Value": 2,
"Description": "A factor by which view range and hearing range will be divided when this NPC is distracted"
},
"AbsoluteDetectionRange": {
"Value": 4,
"Description": "The range at which a target is guaranteed to be detected. If zero, absolute detection will be disabled."
},
"WarnGroups": {
"Value": ["Goblin_Scrapper"],
"Description": "The groups to warn when spotting an enemy"
},
"AttitudeGroup": {
"Value": "Empty",
"Description": "This NPCs attitude group"
},
"Attack": {
"Value": "Root_NPC_Goblin_Ogre_Attack",
"Description": "The attack to use."
},
"AttackDistance": {
"Value": 2,
"Description": "The distance at which an NPC will execute attacks"
},
"AttackPauseRange": {
"Value": [1.5, 2],
"Description": "The range for absolute minimum time before an NPC can execute a second attack (or block)."
},
"CombatRelativeTurnSpeed": {
"Value": 1.5,
"Description": "Modifier that decides turn speed difference in combat."
},
"LeashDistance": {
"Value": 20,
"Description": "The range after which an NPC will start to want to return to their spawn point."
},
"LeashMinPlayerDistance": {
"Value": 4,
"Description": "The minimum distance from the player before the NPC will be willing to give up on the chase."
},
"LeashTimer": {
"Value": [3, 5],
"Description": "How long the NPC must be more than the minimum distance form the player and too far from leash before giving up."
},
"HardLeashDistance": {
"Value": 60,
"Description": "An absolute maximum from the the leash position the NPC can go before turning back."
},
"NameTranslationKey": {
"Value": "server.npcRoles.Template.name",
"Description": "Translation key for NPC name display"
}
},
"Appearance": { "Compute": "Appearance" },
"DropList": { "Compute": "DropList" },
"MaxHealth": { "Compute": "MaxHealth" },
"StartState": "Idle",
"DefaultPlayerAttitude": "Hostile",
"DefaultNPCAttitude": "Ignore",
"AttitudeGroup": { "Compute": "AttitudeGroup" },
"KnockbackScale": 0.5,
"MotionControllerList": [
{
"Type": "Walk",
"MaxWalkSpeed": 3,
"Gravity": 10,
"MaxFallSpeed": 8,
"Acceleration": 10
}
],
"InteractionVars": {
"Melee_Damage": {
"Interactions": [
{
"$Comment": "Config for a successful melee hit, default values for Effects and Hitshape are not overwritten here, see each NPC_ file",
"Parent": "NPC_Attack_Melee_Damage",
"DamageCalculator": {
"Type": "Absolute",
"BaseDamage": {
"Physical": 10
},
"RandomPercentageModifier": 0.1
}
}
]
}
},
"StateTransitions": [
{
"States": [
{
"From": ["Idle"],
"To": ["Sleep"]
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Laydown"
},
{
"Type": "Timeout",
"Delay": [1, 1]
}
]
},
{
"States": [
{
"From": ["Sleep"],
"To": []
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Wake"
},
{
"Type": "Timeout",
"Delay": [1, 1]
}
]
},
{
"States": [
{
"From": ["Idle"],
"To": ["Eat"]
}
],
"Actions": [
{
"Type": "Inventory",
"Operation": "SetHotbar",
"Item": { "Compute": "EatItem" },
"Slot": 2,
"UseTarget": false
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 2,
"UseTarget": false
}
]
},
{
"States": [
{
"From": ["CallRat"],
"To": ["Eat"]
}
],
"Actions": [
{
"Type": "Inventory",
"Operation": "SetHotbar",
"Item": { "Compute": "FoodNPCItem" },
"Slot": 2,
"UseTarget": false
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 2,
"UseTarget": false
}
]
},
{
"States": [
{
"From": ["Eat"],
"To": []
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status"
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 0,
"UseTarget": false
}
]
},
{
"States": [
{
"From": [],
"To": ["Combat"]
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status"
},
{
"Type": "Beacon",
"Message": "Goblin_Ogre_Warn",
"TargetGroups": { "Compute": "WarnGroups" },
"SendTargetSlot": "LockedTarget"
}
]
}
],
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"Instructions": [
{
"Continue": true,
"Sensor": {
"Type": "Any",
"Once": true
},
"Actions": [
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 0,
"UseTarget": false
}
]
},
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": ["Alerted", "Alerted"],
"AlertedRange": { "Compute": "AlertedRange" }
}
},
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Sensor": {
"Type": "State",
"State": ".Default"
},
"Instructions": [
{
"Actions": [
{
"Type": "Random",
"Actions": [
{
"Weight": 10,
"Action": {
"Type": "State",
"State": ".Guard"
}
},
{
"Weight": 10,
"Action": {
"Type": "State",
"State": "Sleep"
}
},
{
"Weight": 10,
"Action": {
"Type": "State",
"State": "Eat"
}
},
{
"Weight": 10,
"Action": {
"Type": "State",
"State": "CallRat"
}
}
]
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": ".Guard"
},
"Instructions": [
{
"Continue": true,
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [5, 10]
},
{
"Type": "State",
"State": ".Default"
}
]
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Follow_Path"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": ["Alerted", "Alerted"],
"AlertedRange": { "Compute": "AlertedRange" }
}
},
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange / DistractedPenalty" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "ViewRange / DistractedPenalty" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Reference": "Component_Instruction_Play_Animation_In_State_For_Duration",
"Modify": {
"_ExportStates": ["Idle.Default"],
"Animation": "Sleep",
"Duration": [5, 6]
}
},
{
"Sensor": {
"Type": "Beacon",
"Message": "Annoy_Ogre",
"Range": 5
},
"Actions": [
{
"Type": "Attack",
"Attack": { "Compute": "SleepingAttack" },
"AttackPauseRange": [1, 2]
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Eat"
},
"Instructions": [
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": ["Alerted", "Alerted"],
"AlertedRange": { "Compute": "AlertedRange" }
}
},
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange / DistractedPenalty" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "ViewRange / DistractedPenalty" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Reference": "Component_Instruction_Play_Animation_In_State_For_Duration",
"Modify": {
"_ExportStates": ["Idle.Default"],
"Animation": "Eat",
"Duration": [5, 6]
}
}
]
},
{
"Sensor": {
"Type": "State",
"State": "CallRat"
},
"Instructions": [
{
"Continue": true,
"Sensor": {
"Type": "Any",
"Once": true
},
"Actions": [
{
"Type": "TriggerSpawnBeacon",
"BeaconSpawn": { "Compute": "FoodNPCBeacon" },
"Range": 15
}
]
},
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": ["Alerted", "Alerted"],
"AlertedRange": { "Compute": "AlertedRange" }
}
},
{
"$Comment": "Check for any hostile targets in range that could alert the NPC",
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"ThroughWalls": false,
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Sensor": {
"Type": "Mob",
"Range": 2.5,
"Filters": [
{
"Type": "NPCGroup",
"IncludeGroups": { "Compute": "FoodNPCGroups" }
},
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Swipe"
},
{
"Type": "Timeout",
"Delay": [0.1, 0.1],
"Action": {
"Type": "Sequence",
"Actions": [
{
"Type": "Remove"
},
{
"Type": "State",
"State": "Eat"
}
]
}
}
]
},
{
"Continue": true,
"Sensor": {
"Type": "Mob",
"Range": 5,
"Filters": [
{
"Type": "NPCGroup",
"IncludeGroups": { "Compute": "FoodNPCGroups" }
},
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
}
},
{
"Reference": "Component_Instruction_State_Timeout",
"Modify": {
"_ExportStates": ["Idle"],
"Delay": [10, 15]
}
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Alerted"
},
"Instructions": [
{
"Reference": "Component_Instruction_Play_Animation",
"Modify": {
"Animation": "Alerted"
}
},
{
"Continue": true,
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AlertedRange" },
"Filters": [
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
}
},
{
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AlertedRange" }
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [1, 1]
},
{
"Type": "State",
"State": "Combat"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Combat"
},
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Chase"
},
"Instructions": [
{
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AttackDistance" },
"Filters": [
{
"Type": "LineOfSight"
}
]
},
"Actions": [
{
"Type": "State",
"State": ".Default"
}
]
},
{
"Reference": "Component_Instruction_Soft_Leash",
"Modify": {
"_ExportStates": ["ReturnHome"],
"LeashDistance": { "Compute": "LeashDistance" },
"LeashMinPlayerDistance": {
"Compute": "LeashMinPlayerDistance"
},
"LeashTimer": { "Compute": "LeashTimer" },
"HardLeashDistance": { "Compute": "HardLeashDistance" }
}
},
{
"Reference": "Component_Instruction_Intelligent_Chase",
"Modify": {
"_ExportStates": ["Search", "Search", "ReturnHome"],
"ViewRange": { "Compute": "AlertedRange * 2" },
"HearingRange": { "Compute": "HearingRange * 2" },
"StopDistance": 0.1,
"RelativeSpeed": 0.5
}
}
]
},
{
"$Comment": "NPC melee attack",
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AttackDistance" },
"Filters": [
{
"Type": "LineOfSight"
}
],
"ActionsBlocking": true,
"Actions": [
{
"Type": "Attack",
"Attack": { "Compute": "Attack" },
"AttackPauseRange": { "Compute": "AttackPauseRange" }
},
{
"$Comment": "Brief delay to prevent the NPC potentially strafing/backing away and missing the shot.",
"Type": "Timeout",
"Delay": [0.2, 0.2]
}
],
"HeadMotion": {
"Type": "Aim",
"RelativeTurnSpeed": { "Compute": "CombatRelativeTurnSpeed" }
}
},
"Actions": [
{
"Type": "State",
"State": ".Chase"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "ReturnHome"
},
"Instructions": [
{
"Sensor": {
"Type": "And",
"Sensors": [
{
"Type": "Damage",
"Combat": true,
"TargetSlot": "LockedTarget",
"Enabled": { "Compute": "AbsoluteDetectionRange > 0" }
},
{
"Type": "Target",
"TargetSlot": "LockedTarget",
"Range": { "Compute": "AbsoluteDetectionRange" }
}
]
},
"Actions": [
{
"Type": "State",
"State": "Combat"
}
]
},
{
"Sensor": {
"Type": "Leash",
"Range": { "Compute": "LeashDistance * 0.3" },
"BodyMotion": {
"Type": "Seek",
"SlowDownDistance": { "Compute": "LeashDistance * 0.4" },
"StopDistance": { "Compute": "LeashDistance * 0.2" },
"RelativeSpeed": 0.8,
"UsePathfinder": true
}
},
"Actions": [
{
"Type": "SetStat",
"Stat": "Health",
"Value": 1000000
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Search"
},
"Instructions": [
{
"Sensor": {
"Type": "Damage",
"Combat": true,
"TargetSlot": "LockedTarget"
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Instructions": [
{
"Sensor": {
"Reference": "Component_Sensor_Lost_Target_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"AbsoluteDetectionRange": {
"Compute": "AbsoluteDetectionRange"
},
"TargetSlot": "LockedTarget"
}
},
"Actions": [
{
"Type": "State",
"State": "Combat"
}
]
},
{
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"AbsoluteDetectionRange": {
"Compute": "AbsoluteDetectionRange"
},
"Attitudes": ["Hostile", "Neutral"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"BodyMotion": {
"Type": "Sequence",
"Motions": [
{
"Type": "Timer",
"Time": [3, 6],
"Motion": {
"Type": "Wander",
"MaxHeadingChange": 1,
"RelativeSpeed": 0.5
}
},
{
"Type": "Sequence",
"Looped": true,
"Motions": [
{
"Type": "Timer",
"Time": [3, 6],
"Motion": {
"Type": "WanderInCircle",
"Radius": 10,
"MaxHeadingChange": 60,
"RelativeSpeed": 0.5
}
},
{
"Type": "Timer",
"Time": [2, 3],
"Motion": {
"Type": "Nothing"
}
}
]
}
]
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [4, 5]
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
}
]
}
],
"NameTranslationKey": { "Compute": "NameTranslationKey" }
}
```
### Goblin\_Ogre\_Tutorial role file
```json
{
"Type": "Variant",
"Reference": "Template_Goblin_Ogre_Tutorial",
"Modify": {
"Appearance": "Goblin",
"MaxHealth": 124,
"InteractionVars": {
"Melee_SwingDown_Damage": {
"Interactions": [
{
"Parent": "Goblin_Ogre_Swing_Down_Damage",
"DamageCalculator": {
"Type": "Absolute",
"BaseDamage": {
"Physical": 20
}
}
}
]
}
},
"NameTranslationKey": { "Compute": "NameTranslationKey" }
},
"Parameters": {
"NameTranslationKey": {
"Value": "server.npcRoles.Goblin_Ogre.name",
"Description": "Translation key for NPC name display"
}
}
}
```
### Goblin\_Ogre\_Swing\_Down file
```json
{
"Type": "Simple",
"Effects": {
"ItemPlayerAnimationsId": "Goblin_Club",
"ItemAnimationId": "SwingDown"
},
"$Comment": "Prepare Delay",
"RunTime": 0.2,
"Next": {
"Type": "Selector",
"$Comment": "Length of Combat",
"RunTime": 0.25,
"Selector": {
"Id": "Horizontal",
"Direction": "ToLeft",
"TestLineOfSight": true,
"ExtendTop": 0.5,
"ExtendBottom": 0.5,
"StartDistance": 1,
"EndDistance": 2.5,
"Length": 90,
"RollOffset": 60,
"YawStartOffset": -45
},
"HitEntity": {
"Interactions": [
{
"Type": "Replace",
"DefaultValue": {
"Interactions": ["Goblin_Ogre_Swing_Down_Damage"]
},
"Var": "Melee_SwingDown_Damage"
}
]
},
"Next": {
"Type": "Simple",
"$Comment": "Pad the interaction length",
"RunTime": 0.1
}
}
}
```
### Root\_NPC\_Goblin\_Ogre\_Attack
(you need this file to exist, because template is referencing it)
```json
{
"Interactions": [
{
"Type": "Chaining",
"ChainId": "Slashes",
"ChainingAllowance": 15,
"Next": [
"Goblin_Ogre_Swing_Left",
"Goblin_Ogre_Swing_Right",
"Goblin_Ogre_Swing_Down"
]
}
],
"Tags": {
"Attack": ["Melee"]
}
}
```
--- 8 - Feeding the Ogre
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/8-food
Lest we forget, there was another significantly more complex idle behaviour we have to implement:
* Standing guard (we already did this!)
* Napping for a while (this one too!)
* **Going off to find some food.**
This one might be a bit trickier. When we pick this behaviour we need to scan the area to see if there is any food. If we find it, we head over, pretend to grab it, and then set to eating.
But what if we *don't* find any food? We should probably still pretend to go looking for it - it's not like this ogre is omniscient and already knows there's no food where it expects it because the sensor said there wasn't...*right?*
We start by creating our **.FindFood** substate immediately after **.Guard**.
```json
{
"Sensor": {
"Type": "State",
"State": ".Guard"
},
"Instructions": [ ... ],
},
{
"Sensor": {
"Type": "State",
"State": ".FindFood"
},
"Instructions": [ ... ]
}
```
We then add it to our list of random actions.
```json
"Type": "Random",
"Actions": [
{
"Weight": 70,
"Action": {
"Type": "State",
"State": ".Guard"
}
},
{
"Weight": 20,
"Action": {
"Type": "State",
"State": "Sleep"
}
},
{
"Weight": 10,
"Action": {
"Type": "State",
"State": ".FindFood"
}
}
]
```
Next we want to perform the actual search for food. But what *is* food? And not in a philosophical sense. What kinds of food does this ogre look for? This is another point where we might want to have a chat with the designers. Do they want it to go after multiple different things? Or is there one particular type of food a goblin ogre is interested in?
A quick discussion with our designers reveals we've been working under an incorrect assumption and highlights the importance of ensuring that we thoroughly understand both the contents of the design specifications, and the *intent behind them*. This ogre is meant to be a guard and shouldn't leave his post. He should never actively set off to find food, but rather whip something out of a pocket and eat it on the spot.
Okay. That makes things significantly easier! We can now cross off one of our idle substates:
* .Default (pick the random behaviour state to switch to)
* .Guard (stand guard)
* ~~.FindFood (go search for some nearby food if it exists)~~
* .EatRat (murder an innocent nearby rat - we're still ignoring this for now)
So let's remove **.FindFood** and jump straight to **Eat**, adding the same logic we added to the other idle states. We also need to remember to update the reference in the list of random actions so that it points to the correct state!
```json
{
"Sensor": {
"Type": "State",
"State": "Eat"
},
"Instructions": [
{
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [5, 10]
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
}
```
We don't actually know for sure what food this ogre has in his pockets and it's probably something that might get changed in the future, or be nice to make easily changeable in variants.
To accommodate that, let's add an **EatItem** parameter and accompanying description to the parameters block.
```json
"Parameters": {
"Appearance": {
"Value": "Bear_Grizzly",
"Description": "Model to be used"
},
"EatItem": {
"Value": "Food_Beef_Raw",
"Description": "The item this NPC will find when it rummages for food"
},
"DropList": {
"Value": "Empty",
"Description": "Drop Items"
}
}
```
Now we just need to set up another **PlayAnimation** action, much like in the **Sleep** state, to handle the eating itself.
```json
{
"Sensor": {
"Type": "State",
"State": "Eat"
},
"Instructions": [
{
"Continue": true,
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [5, 10]
},
{
"Type": "State",
"State": "Idle"
}
]
},
{
"Reference": "Component_Instruction_Play_Animation",
"Modify": {
"Animation": "Eat"
}
}
]
}
```
This looks *awfully* familiar... Could this be something we want to do often in many NPCs? Might we frequently want to play an animation for a specific random duration? I'd say yes, so let's make it a component!
```json
{
"Type": "Component",
"Class": "Instruction",
"Parameters": {
"_ImportStates": ["Main"],
"Animation": {
"Value": "",
"Description": "The animation to play"
},
"Duration": {
"Value": [3, 5],
"Description": "The amount of time to wait before transitioning"
}
},
"Content": {
"Continue": true,
"Instructions": [
{
"Reference": "Component_Instruction_State_Timeout",
"Modify": {
"_ExportStates": ["Main"],
"Delay": { "Compute": "Duration" }
}
},
{
"Reference": "Component_Instruction_Play_Animation",
"Modify": {
"Animation": { "Compute": "Animation" }
}
}
]
}
}
```
I'm calling this one `Component_Instruction_Play_Animation_In_State_For_Duration`. The Timeout instruction is replaced with the component made in the previous interlude.
Now we need to apply it to both places in the ogre template.
```json
"Sensor": {
"Type": "State",
"State": "Eat"
},
"Instructions": [
{
"Reference": "Component_Instruction_Play_Animation_In_State_For_Duration",
"Modify": {
"_ExportStates": ["Idle.Default"],
"Animation": "Eat",
"Duration": [15, 20]
}
}
]
```
Only **Eat** is pictured here, but you can imagine the same changes being made to **Sleep** too.
Now why aren't we converting the whole state itself into a component? There's a pretty good reason behind that: we haven't added entity detection yet and we almost surely will. This will need to live in the state alongside the other logic, so we can't exactly push the whole state itself into a component.
There's one last thing we need to do to make this ogre actually eat - take out the food and put it away again. Since we don't want to somehow risk ending up in a state where the ogre is trying to beat its enemies with a chunk of meat or eat its own weapon, we'll do this with state transitions.
```json
"StateTransitions": [
...
{
"States": [
{
"From": ["Idle"],
"To": ["Eat"]
}
],
"Actions": [
{
"Type": "Inventory",
"Operation": "SetHotbar",
"Item": { "Compute": "EatItem" },
"Slot": 2,
"UseTarget": false
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 2,
"UseTarget": false
}
]
}
]
```
There's a few important things to note here. This combination of actions will place the item from the **EatItem** parameter we defined earlier into slot 2 of its hotbar, and then switch to using that slot. We have to define it as **UseTarget: false** to ensure that it acts on the ogre itself.
By default, NPCs have three hotbar slots, so we've set it to use the last slot for this purpose (slots are numbered starting from zero).
So this handles actually pulling out the food to eat it, but not putting it away again afterwards. Let's add that too.
```json
{
"States": [
{
"From": ["Eat"],
"To": []
}
],
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status"
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 0,
"UseTarget": false
}
]
}
```
Now our ogre will happily pull out a chunk of meat, start eating it, and then put it away again when it's done!
Perfect! The last thing we'll do, just to be sure we don't end up in any strange states if the goblin unloads while eating, is make sure we also switch to the correct weapon at the beginning of the idle state.
```json
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"Instructions": [
{
"Continue": true,
"Sensor": {
"Type": "Any",
"Once": true
},
"Actions": [
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 0,
"UseTarget": false
}
]
},
...
]
}
```
With that, we have all the idle behaviours that don't rely on other NPCs set up. We'll deal with that next!
--- 4 - Drawing the rest of the ogre
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/4-drawing-the-ogre
We were fortunate, in a sense, that the initial ogre behaviour was simple to handle with existing components.
Now the real challenge begins! From here on out, it's critical to remember to test, test, and test some more. **Every time we add a new behaviour it should be tested to ensure it works as expected**. Never leave it until everything is implemented - this just results in unnecessary headaches.
First, we have to figure out how we want to randomise these base idle behaviours. There are three we care about right now since they don't involve interacting with any other types of NPC:
* Standing guard (we already did this!)
* Napping for a while.
* Going off to find some food.
There are a few ways we can approach this, but we'll be using a Random Action to make the initial pick and then letting the individual behaviour control its length before resetting and picking a new one.
With this in mind, we can see a minor error in our original judgement - the **Idle** state needs **four** substates, not three:
* **.Default** (this now becomes an entry state for picking the random behaviour state to switch to)
* **.Guard** (this is now our guarding state)
* **.FindFood** (go search for some nearby food if it exists)
* **.EatRat** (murder an innocent nearby rat - we ignore this for now since it relies on another NPC)
So let's quickly refactor the existing **Idle** state to reflect this change, and add a **Random Action** that will, for now, only have a single option.
```json
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Default"
},
"Instructions": [
{
"Actions": [
{
"Type": "Random",
"Actions": [
{
"Weight": 100,
"Action": {
"Type": "State",
"State": ".Guard"
}
}
]
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": ".Guard"
},
"Instructions": [
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Follow_Path"
}
]
}
]
```
Looking at the modified **Idle** state instruction, we now have two substates: **.Default** and **.Guard**.
If we spawn this NPC next to a path marker, it'll act exactly as it did before. The difference now is that it first executes the **Random** action in the **.Default** substate which picks the only available state action and sends the ogre to **.Guard**. We've set the **Weight** to be 100 for now, but it doesn't matter since there are no other choices anyway and we'll balance these better later (perhaps even for months after initial implementation!)
Now that we have more than one substate, it becomes a little harder to understand what the ogre is doing, so we'll add a **Debug** flag at the top of the file that will tell us what state it's in.
```json
"Debug": "DisplayState",
```
Now we can be sure our (presently) friendly goblin ogre is in the correct state.
But wait! There's nothing to send the ogre back to try and do something different!
This is actually pretty simple; we'll just add an instruction with **ActionsBlocking**, a **Timeout**, and **Continue** to the **.Guard** substate that will send it back to **.Default** after a randomised amount of time so it can make a new pick.
```json
{
"Sensor": {
"Type": "State",
"State": ".Guard"
},
"Instructions": [
{
"Continue": true,
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [15, 30]
},
{
"Type": "State",
"State": ".Default"
}
]
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Follow_Path"
}
]
}
```
The **ActionsBlocking** is important here because it ensures we don't execute the switch between states until the first action is complete (the **Timeout**). So now we'll hang around at the path marker for between **fifteen** and **thirty** seconds before going back to pick something else to do.
...not that there's anything else to do yet, so why don't we address that next?
--- 11 - Melee Combat
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/11-melee-combat
For the purpose of this Tutorial we'll focus only on the melee attack. We'll start by making an **attack sequence**. Our NPCs can attack using smart decision making with a feature called the Combat Action Evaluator (CAE), but for this tutorial we'll just stick to simple sequences.
This will also allow for a quick look into the adjacent Interactions system. First we need to create a **Root Interaction** in **HytaleAssets/Server/Item/RootInteractions**, together with other Root interactions. Let's make a **Root\_NPC\_Goblin\_Ogre\_Attack** interaction:
```json
{
"Interactions": [
{
"Type": "Chaining",
"ChainId": "Slashes",
"ChainingAllowance": 15,
"Next": [
"Goblin_Ogre_Swing_Left",
"Goblin_Ogre_Swing_Right",
"Goblin_Ogre_Swing_Down"
]
}
],
"Tags": {
"Attack": ["Melee"]
}
}
```
This is a simple chaining Interaction where the ogre will perform **Swing\_Left** and, so long as the next attack happens within 15 seconds, he will use **Swing\_Right**. If he then manages to attack us a third time within 15 seconds he'll use the **Swing\_Down** attack.
These swing interactions need to be created in the **HytaleAssets/Server/Item/Interactions** folder. Let's make the **Goblin\_Ogre\_Swing\_Left** interaction:
```json
{
"Type": "Simple",
"Effects": {
"ItemPlayerAnimationsId": "Goblin_Club",
"ItemAnimationId": "SwingLeft"
},
"$Comment": "Prepare Delay",
"RunTime": 0.2,
"Next": {
"Type": "Selector",
"$Comment": "Length of Combat",
"RunTime": 0.25,
"Selector": {
"Id": "Horizontal",
"Direction": "ToLeft",
"TestLineOfSight": true,
"ExtendTop": 0.5,
"ExtendBottom": 2,
"StartDistance": 0.1,
"EndDistance": 3.5,
"Length": 60,
"RollOffset": 0,
"YawStartOffset": -30
},
"HitEntity": {
"Interactions": [
{
"Parent": "DamageEntityParent",
"DamageCalculator": {
"BaseDamage": {
"Physical": 8
}
},
"DamageEffects": {
"Knockback": {
"Force": 0.5,
"RelativeX": -5,
"RelativeZ": -5,
"VelocityY": 5
},
"WorldSoundEventId": "SFX_Unarmed_Impact",
"WorldParticles": [
{
"SystemId": "Impact_Blade_01"
}
]
}
}
]
},
"Next": {
"Type": "Simple",
"$Comment": "Pad the interaction length",
"RunTime": 0.1
}
}
}
```
Now we need to approach the target and use our fancy attack sequence. Again, there are a few components that will make most of this much simpler to do:
* `Component_Instruction_Soft_Leash` will work in conjunction with an external **ReturnHome** state to send the ogre back to his start point if we get him too far away.
* `Component_Instruction_Intelligent_Chase` will handle smartly chasing the target based on its last known position and will trigger pathfinding where necessary. We might want to add an extra **Search** state for this.
Let's create the states we need. First, Combat itself, but since we're going to be using `Component_Instruction_Intelligent_Chase`, this component needs to know where to switch when the Target is lost or if the NPC is too far away. Let's create two states: **Search** and **ReturnHome** that will be used there.
```json
{
"Sensor": {
"Type": "State",
"State": "Combat"
},
"Instructions": [],
},
{
"Sensor": {
"Type": "State",
"State": "ReturnHome"
},
"Instructions": []
},
{
"Sensor": {
"Type": "State",
"State": "Search"
},
"Instructions": []
}
```
Let's start with chasing the target. We wrap that behavior in sub state **.Chase** so we can fall back to it when the target gets out of attack range and the NPC needs to run after it in a somewhat intelligent manner.
```json
{
"Sensor": {
"Type": "State",
"State": "Combat"
},
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Chase"
},
"Instructions": [
{
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AttackDistance" },
"Filters": [
{
"Type": "LineOfSight"
}
]
},
"Actions": [
{
"Type": "State",
"State": ".Default"
}
]
},
{
"Reference": "Component_Instruction_Soft_Leash",
"Modify": {
"_ExportStates": ["ReturnHome"],
"LeashDistance": { "Compute": "LeashDistance" },
"LeashMinPlayerDistance": { "Compute": "LeashMinPlayerDistance" },
"LeashTimer": { "Compute": "LeashTimer" },
"HardLeashDistance": { "Compute": "HardLeashDistance" }
}
},
{
"Reference": "Component_Instruction_Intelligent_Chase",
"Modify": {
"_ExportStates": ["Search", "Search", "ReturnHome"],
"ViewRange": { "Compute": "AlertedRange * 2" },
"HearingRange": { "Compute": "HearingRange * 2" },
"StopDistance": 0.1,
"RelativeSpeed": 0.5
}
}
]
}
]
}
```
* First, it checks if the target is within AttackDistance. If so, we switch to the default combat state.
* If the NPC gets too far away from its leash position (often the spawn position), we use `Component_Instruction_Soft_Leash` to send it home. We put this sensor in front of the chase state so that we don't continue chasing if the leash kicks in. There's a **Leash Timer** on this component that decides when it's time to give up.
* And finally the chase component itself, which performs 'intelligent' chasing of the target.
When the Target is within the **AttackDistance**, we perform the attack, otherwise we switch to the chase state. Let's start with the case where the target is within a melee range.
```json
{
"Sensor": {
"Type": "State",
"State": "Combat"
},
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Chase"
},
"Instructions": [ ... ]
},
{
"$Comment": "NPC melee attack",
"Sensor": {
"Type": "Target",
"Range": { "Compute": "AttackDistance" },
"Filters": [
{
"Type": "LineOfSight"
}
]
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "Attack",
"Attack": { "Compute": "Attack" },
"AttackPauseRange": { "Compute": "AttackPauseRange" }
},
{
"Comment": "Brief delay to prevent the NPC potentially strafing/backing away and missing the shot.",
"Type": "Timeout",
"Delay": [0.2, 0.2]
}
],
"HeadMotion": {
"Type": "Aim",
"RelativeTurnSpeed": { "Compute": "CombatRelativeTurnSpeed" }
}
},
{
"Actions": [
{
"Type": "State",
"State": ".Chase"
}
]
}
]
},
```
The important things to note here are the **AttackDistance**, **AttackPauseRange** and **CombatRelativeTurnSpeed**. We're going to configure both of these via the template itself.
**AttackDistance** refers to the distance at which the ogre will attempt to perform melee attacks to hit the target (though the actual range of the attack itself is defined in the **Attack** interaction). **CombatRelativeTurnSpeed** allows us to define how quickly (or slowly) the ogre rotates while in combat. **AttackPauseRange** defines how often the attacks will be performed a bit like an attack cooldown.
Let's make sure we have all these parameters added in the list:
```json
"Attack": {
"Value": "Root_NPC_Goblin_Ogre_Attack",
"Description": "The attack to use."
},
"AttackDistance": {
"Value": 2,
"Description": "The distance at which an NPC will execute attacks"
},
"AttackPauseRange": {
"Value": [1.5, 2],
"Description": "the range for absolute minimum time before an NPC can execute a second attack (or block)."
},
"CombatRelativeTurnSpeed": {
"Value": 1.5,
"Description": "Modifier that decides turn speed difference in combat."
},
"LeashDistance": {
"Value": 20,
"Description": "The range after which an NPC will start to want to return to their spawn point."
},
"LeashMinPlayerDistance": {
"Value": 4,
"Description": "The minimum distance from the player before the NPC will be willing to give up on the chase."
},
"LeashTimer": {
"Value": [3, 5],
"Description": "How long the NPC must be more than the minimum distance form the player and too far from leash before giving up."
},
"HardLeashDistance": {
"Value": 60,
"Description": "An absolute maximum from the the leash position the NPC can go before turning back."
},
```
Now the NPC will attack, but we still need to add **Search** and **ReturnHome** logic. We can do that right away, so we have a fully functional melee ogre.
```json
{
"Sensor": {
"Type": "State",
"State": "ReturnHome"
},
"Instructions": [
{
"Sensor": {
"Type": "And",
"Sensors": [
{
"Type": "Damage",
"Combat": true,
"TargetSlot": "LockedTarget"
},
{
"Enabled": { "Compute": "AbsoluteDetectionRange > 0" },
"Type": "Target",
"TargetSlot": "LockedTarget",
"Range": { "Compute": "AbsoluteDetectionRange" }
}
]
},
"Actions": [
{
"Type": "State",
"State": "Combat"
}
]
},
{
"Sensor": {
"Type": "Leash",
"Range": { "Compute": "LeashDistance * 0.3" }
},
"BodyMotion": {
"Type": "Seek",
"SlowDownDistance": { "Compute": "LeashDistance * 0.4" },
"StopDistance": { "Compute": "LeashDistance * 0.2" },
"RelativeSpeed": 0.8,
"UsePathfinder": true
}
},
{
"Actions": [
{
"Type": "SetStat",
"Stat": "Health",
"Value": 1000000
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
},
```
There are 3 parts to this state: first, it checks if there is incoming combat damage and will switch to Combat if so. We don't want to leave NPCs exploitable as they're running home by having them ignore all attacks. If there's no incoming damage it will find its way home using **BodyMotion: Seek**. We need to be careful here though, since it might become expensive due to **"UsePathfinder": true** turning on complex pathfinding. The last block simply heals the NPC to full health and moves it to idle once it's found its home spot again.
The Search state makes use of `Component_Sensor_Lost_Target_Detection`.
```json
{
"Sensor": {
"Type": "State",
"State": "Search"
},
"Instructions": [
{
"Sensor": {
"Type": "Damage",
"Combat": true,
"TargetSlot": "LockedTarget"
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"Instructions": [
{
"Sensor": {
"Reference": "Component_Sensor_Lost_Target_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"TargetSlot": "LockedTarget"
}
},
"Actions": [
{
"Type": "State",
"State": "Combat"
}
]
},
{
"Sensor": {
"Reference": "Component_Sensor_Standard_Detection",
"Modify": {
"ViewRange": { "Compute": "ViewRange" },
"ViewSector": { "Compute": "ViewSector" },
"HearingRange": { "Compute": "HearingRange" },
"AbsoluteDetectionRange": { "Compute": "AbsoluteDetectionRange" },
"Attitudes": ["Hostile", "Neutral"]
}
},
"Actions": [
{
"Type": "State",
"State": "Alerted"
}
]
},
{
"BodyMotion": {
"Type": "Sequence",
"Motions": [
{
"Type": "Timer",
"Time": [3, 6],
"Motion": {
"Type": "Wander",
"MaxHeadingChange": 1,
"RelativeSpeed": 0.5
}
},
{
"Type": "Sequence",
"Looped": true,
"Motions": [
{
"Type": "Timer",
"Time": [3, 6],
"Motion": {
"Type": "WanderInCircle",
"Radius": 10,
"MaxHeading Change": 60,
"RelativeSpeed": 0.5
}
},
{
"Type": "Timer",
"Time": [2, 3],
"Motion": {
"Type": "Nothing"
}
}
]
}
]
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [4, 5]
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
}
]
}
```
The search state will first check if there is incoming damage and will switch to the alerted state if so. If not it'll execute the next block of instructions using pre-existing components:
* Check if the lost target is detected with the **Lost Target detection sensor**. If so, switch to combat.
* Check if any other target is available through the **Standard detection sensor**. If so, switch to the alerted state.
* Otherwise the NPC will move around using the **Wander** motion, stopping briefly in between until it gives up and...
* ...goes back to being idle.
--- 3 - The importance of being idle
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/3-importance-of-being-idle
There's no particular rule for the order in which we should tackle states either, but I like to start with the **Idle** state.
First we need to add the state to the template. We do this in two steps.
1. Set up the **Idle** state in the instructions:
```json
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"BodyMotion": {
"Type": "Nothing"
}
}
]
```
Here we've added the state sensor and corresponding instruction that will hold and define the contents of the **Idle** state.
2. Set the default starting state for the NPC:
```json
"Appearance": { "Compute": "Appearance" },
"DropList": { "Compute": "DropList" },
"MaxHealth": { "Compute": "MaxHealth" },
"StartState": "Idle",
```
This is a simple case of adding the **StartState** line alongside the other header fields and setting it to be the **Idle** state.
Now we add the first substate within this, which we already decided would just be a plain **.Default** substate.
```json
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Default"
},
"Instructions": []
}
]
}
]
```
This functions the same way as its parent **Idle** state, but we don't need to specify it as a starting state anywhere because it's using the default name.
The first behaviour we're adding here is the 'protect the entrance of the Goblin POI' behaviour. We already identified an existing component we can use for this, so let's add that immediately.
```json
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": ".Default"
},
"Instructions": [
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Follow_Path"
}
]
}
]
```
If we want, we can use a **Modify** block to set a range within which to follow this path or a particular way to follow it, but since we only want it to stand guard and expect it to spawn near its target marker, we can ignore both of those for now.
Straight away we can jump in-game and spawn our ogre using **/npc spawn Goblin\_Ogre**. If we add a single path marker with **/path new Test**, he'll happily go and stand guard there without doing anything else.
And there we go! First idle behaviour is now complete!
--- 5 - NPC Sleep Behavior
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/5-sleep-behavior
Let's add a new top level **Sleep** state. For illustrative purposes, the Idle state has been truncated.
```json
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"Instructions": [ ... ],
},
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [ ... ]
}
```
And now we add another action to the **Random** action list to move to the **Sleep** state.
```json
{
"Sensor": {
"Type": "State",
"State": ".Default"
},
"Instructions": [
{
"Actions": [
{
"Type": "Random",
"Actions": [
{
"Weight": 80,
"Action": {
"Type": "State",
"State": ".Guard"
}
},
{
"Weight": 20,
"Action": {
"Type": "State",
"State": "Sleep"
}
}
]
}
]
}
]
}
```
Notice the weights in this case - there's a 20% chance that the ogre will go to sleep any time it decides to switch behaviour, but an 80% chance that it'll just keep standing guard instead.
We still need to actually add the sleeping behaviour, but first we'll add Instruction with the **Timeout** action to the beginning of the **Sleep** state, just like we did with **.Guard**. We just remove **"Continue": true** because we only have one instruction.
--- 6 - A brief interlude
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/6-a-brief-interlude
We already have the logic to build this component - it's already contained within the **.Guard** state we created earlier. All we need to do is identify which parts of it we need to expose to whoever might want to use this component in the future.
That's pretty simple too - we have two:
* The duration of the delay before switching state.
* The state to switch to.
So we put together our new component (`Component_Instruction_State_Timeout`).
```json
{
"Type": "Component",
"Class": "Instruction",
"Parameters": {
"_ImportStates": ["Main"],
"Delay": {
"Value": [3, 5],
"Description": "The amount of time to wait before transitioning"
}
},
"Content": {
"Continue": true,
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": { "Compute": "Delay" }
},
{
"Type": "ParentState",
"State": "Main"
}
]
}
}
```
The logic here should be very familiar - the only difference is that we've added and described the parameters in the **Parameters** block, and now use a **ParentState** action to signify that this is going to use one of the **\_ImportedStates** from the template itself.
If we would update the template and replace the Timeout block with it, it would look like this, for example, in the **Sleep** state.
```json
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [
{
"Reference": "Component_Instruction_State_Timeout",
"Modify": {
"_ExportStates": ["Idle.Default"],
"Delay": [30, 45]
}
}
]
}
```
We won't replace this everywhere in the tutorial just yet, but we'll use this knowledge to replace even more code with components.
--- 9 - Inter-NPC Interaction
Source: https://hytalemodding.dev/en/docs/official-documentation/npc/9-inter-npc-interaction
Now we need to start thinking about how to get our ogre to respond to other NPCs in the world. We won't think about combat or dealing with players yet, but there are a couple of other inter-NPC behaviours described in the design specification:
* Whack annoying goblin scrappers while sleeping.
* Grab and eat rats that come too close.
We'll start with the first because there are a number of problems that are going to come into play when we tackle the second.
Whacking goblins takes place during the **Sleep** state, so we'll be doing all our editing there. We don't actually need to add any detection for this, because we're going to trust the goblin scrappers to *tell* us they're being annoying. This is something pretty neat about using beacons to communicate between NPCs.
When one of the NPCs sends a message, it can trigger behaviour in the other. Basically, this means that the **Goblin Scrapper** is going to handle the bulk of this behaviour. When they go into annoy mode, they'll approach the ogre. When they're close enough, they'll send a message telling him they're being annoying, and he'll then randomly swat at them in his sleep.
They're essentially actually annoying him with their message!
```json
{
"Sensor": {
"Type": "State",
"State": "Sleep"
},
"Instructions": [
...
{
"Sensor": {
"Type": "Beacon",
"Message": "Annoy_Ogre",
"Range": 5
},
"Actions": []
}
]
}
```
This sensor listens for the **Annoy\_Ogre** message and will perform its actions when receiving it, so long as it comes from an NPC that's close by. The nice thing about this is that it can respond to any NPC that decides to annoy it!
Next we'll add an attack to it. It'll be the animators' responsibility to ensure that this meshes nicely with the sleeping animation, and we'll also expose the attack name as a parameter in the parameters block.
```json
"Parameters": {
...
"SleepingAttack": {
"Value": "Root_NPC_Attack_Melee",
"Description": "Attack to use on NPCs that annoy it while sleeping"
},
"DropList": {
"Value": "Empty",
"Description": "Drop Items"
}
}
```
We use a placeholder attack because we don't have a real one yet.
```json
{
"Sensor": {
"Type": "Beacon",
"Message": "Annoy_Ogre",
"Range": 5
},
"Actions": [
{
"Type": "Attack",
"Attack": { "Compute": "SleepingAttack" },
"AttackPauseRange": [1, 2]
}
]
}
```
We don't need another NPC to test this - we can use **/npc message Annoy\_Ogre** to trigger this behaviour while looking at the ogre.
Now that our ogre can whack those pesky scrappers, the time has come to address the rat in the room: grabbing other NPCs is **hard**. Just in general. We don't have that kind of capability in tech and there's no guarantee we will either.
A quick chat with our designers resulted in the following specification:
* Spawn a rat at some location.
* Have it run past the goblin.
* Have it animate to grab the rat and eat it.
This clarifies things, but doesn't resolve the problems. To do this, we're going to have to be creative.
First we need to actually spawn the rat somewhere near the ogre and get it to head over to be seized and eaten. We can do something like this by using a form of **manual spawn beacon** - an entity type which has to be placed in the world and can be triggered by other nearby NPCs on demand. We'll also need to build a small template for the rat itself to get it to move to the ogre.
This seems like a behaviour that could be pretty reusable, so we'll make this template as simple and generic as possible so that it can be used in conjunction with all sorts of NPCs that are meant to grab small creatures and eat them.
We'll call this `Template_Edible_Critter`.
```json
{
"Type": "Abstract",
"KnockbackScale": 0.5,
"Parameters": {
"Appearance": {
"Value": "Rat",
"Description": "Model to be used"
},
"WalkSpeed": {
"Value": 3,
"Description": "How fast this critter moves"
},
"SeekRange": {
"Value": 40,
"Description": "How far this NPC is allowed to be from the target that will eat"
},
"MaxHealth": {
"Value": 100,
"Description": "Max health for the NPC"
},
"NameTranslationKey": {
"Value": "server.npcRoles.Template.name",
"Description": "Translation key for NPC name display"
}
},
"Appearance": { "Compute": "Appearance" },
"StartState": "Idle",
"MotionControllerList": [
{
"Type": "Walk",
"MaxWalkSpeed": { "Compute": "WalkSpeed" },
"Gravity": 10,
"MaxFallSpeed": 8,
"Acceleration": 10
}
],
"MaxHealth": { "Compute": "MaxHealth" },
"Instructions": [
{
"Instructions": [
{
"Sensor": {
"Type": "State",
"State": "Idle"
},
"Instructions": [
{
"Sensor": {
"Type": "Beacon",
"Message": "Approach_Target",
"TargetSlot": "LockedTarget"
},
"Actions": [
{
"Type": "State",
"State": "Seek"
}
]
},
{
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [1, 1]
},
{
"Type": "Despawn"
}
]
}
]
},
{
"Sensor": {
"Type": "State",
"State": "Seek"
},
"Instructions": [
{
"Sensor": {
"Type": "Target",
"TargetSlot": "LockedTarget",
"Range": { "Compute": "SeekRange" }
},
"BodyMotion": {
"Type": "Seek",
"SlowDownDistance": 0.1,
"StopDistance": 0.1
}
},
{
"ActionsBlocking": true,
"Actions": [
{
"Type": "Timeout",
"Delay": [1, 1]
},
{
"Type": "State",
"State": "Idle"
}
]
}
]
}
]
}
],
"NameTranslationKey": { "Compute": "NameTranslationKey" }
}
```
This is a really simple NPC. It allows setting an **Appearance**, **WalkSpeed**, and **SeekRange** so that it can encompass a variety of different critter types. If idle for too long, it'll despawn, otherwise it'll try and seek towards the NPC that's meant to eat it, either from being signalled with a beacon or from being spawned in that specific state with a specific target already defined.
In this case, we want to do the latter, so let's define a **variant** (**Edible\_Rat**).
```json
{
"Type": "Variant",
"Reference": "Template_Edible_Critter",
"Modify": {
"Appearance": "Rat",
"WalkSpeed": 5,
"MaxHealth": 100,
"NameTranslationKey": { "Compute": "NameTranslationKey" }
},
"Parameters": {
"NameTranslationKey": {
"Value": "server.npcRoles.Rat.name",
"Description": "Translation key for NPC name display"
}
}
}
```
...and a **spawn beacon** (also called **Edible\_Rat**) that can create it for us.
```json
{
"Environments": [],
"NPCs": [
{
"Weight": 1,
"Id": "Edible Rat"
}
],
"SpawnAfterGameTimeRange": ["PT5M", "PT10M"],
"NPCSpawnState": "Seek",
"TargetSlot": "LockedTarget"
}
```
This simple config will only spawn the rat NPC we defined and will set it to the correct state. The **SpawnAfterGameTimeRange** parameter is required for the configuration to be used in other contexts, but isn't actually useful to us here.
Now, if we use **/spawning beacons add Edible\_Rat --manual** we can create this spawn beacon ready for our ogre to use! Level designers who want to use this behaviour will need to add this beacon somewhere in the prefab so that the rats can be spawned, but multiple ogres can share the same beacon for this purpose. We can use **/spawning beacons trigger** to make sure it works!
Now we just need to set up our ogre to actually spawn the rat and fake the interaction between them.
As always, we start by adding the state (**CallRat** - we're actually going to make it a main state instead of using the originally planned **.EatRat** substate to take advantage of some existing states and state transitions as we talked about earlier)...
```json
{
"Sensor": {
"Type": "State",
"State": "Eat"
},
...
},
{
"Sensor": {
"Type": "State",
"State": "CallRat"
},
"Instructions": [ ]
}
```
...and an action to the random list.
```json
{
"Type": "Random",
"Actions": [
{
"Weight": 60,
"Action": {
"Type": "State",
"State": ".Guard"
}
},
...
{
"Weight": 10,
"Action": {
"Type": "State",
"State": "CallRat"
}
}
]
}
```
Our **CallRat** state will just handle triggering the beacon and then waiting for a little while to see if the rat successfully arrives. If it doesn't, we'll reset and go back to pick another idle state.
Faking picking up and eating the rat we'll handle by **removing** the rat once it gets close enough and then giving the ogre an **item to hold** just like we did with the previous eat state transition.
The only difference here is the item being eaten (and thus the specific transition itself), so we can actually just make use of the previous **Eat** state for handling that side of the logic!
Before we can go any further though, we need to define an **NPC group** (**Edible\_Rat** again) containing our edible rat and make it a parameter on the ogre so it can find it!
```json
{
"IncludeRoles": ["Edible_Rat"]
}
```
And now we make this an exposed parameter on the ogre template. While we're at it, let's also add a parameter containing the name of the manual spawn beacon we'll trigger.
```json
"Parameters": {
...
"FoodNPCGroups": {
"Value": ["Edible_Rat"],
"Description": "The groups of edible NPCs that will come from triggering the beacon"
},
"FoodNPCBeacon": {
"Value": "Edible_Rat",
"Description": "The spawn beacon to trigger to create an edible NPC"
},
...
}
```
With this done, we can implement the **CallRat** state logic and test that it works.
```json
{
"Sensor": {
"Type": "State",
"State": "CallRat"
},
"Instructions": [
{
"Continue": true,
"Sensor": {
"Type": "Any",
"Once": true
},
"Actions": [
{
"Type": "TriggerSpawnBeacon",
"BeaconSpawn": { "Compute": "FoodNPCBeacon" },
"Range": 15
}
]
},
{
"Sensor": {
"Type": "Mob",
"Range": 2.5,
"Filters": [
{
"Type": "NPCGroup",
"IncludeGroups": { "Compute": "FoodNPCGroups" }
},
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"ActionsBlocking": true,
"Actions": [
{
"Type": "PlayAnimation",
"Slot": "Status",
"Animation": "Swipe"
},
{
"Type": "Timeout",
"Delay": [0.1, 0.1],
"Action": {
"Type": "Sequence",
"Actions": [
{
"Type": "Remove"
},
{
"Type": "State",
"State": "Eat"
}
]
}
}
]
},
{
"Continue": true,
"Sensor": {
"Type": "Mob",
"Range": 5,
"Filters": [
{
"Type": "NPCGroup",
"IncludeGroups": { "Compute": "FoodNPCGroups" }
},
{
"Type": "LineOfSight"
}
]
},
"HeadMotion": {
"Type": "Watch"
}
},
{
"Reference": "Component_Step_State_Timeout",
"Modify": {
"_ExportStates": ["Idle"],
"Delay": [10, 15]
}
}
]
}
```
The logic here is pretty straightforward - we start by triggering the beacon we defined in the parameters exactly once and continuing (as defined by the **Continue** and **Once** flags). We then wait for the edible NPC matching the list of groups we provided in the parameter (which contains only our edible rat in this instance) to get close enough to grab and then play an animation with a brief delay before removing it and moving to the **Eat** state. We watch it for a little bit until it gets close enough so it doesn't look too bad. Note how that last pair of actions (enclosed in a **sequence**) is not marked as blocking - both will execute in the same tick.
If the rat never arrives, we give it about 10-15 seconds before giving up and returning to **Idle**.
Now that it's working, let's implement the state transitions to make this goblin actually 'grab' the rat and start eating it. We don't have the 'rat' item yet, so we'll just use a different item as a placeholder for now.
We need to define this in the parameters too.
```json
"Parameters": {
...
"FoodNPCItem": {
"Value": "Food_Cheese",
"Description": "The edible NPC in item form"
},
...
}
```
And then a single state transition from **CallRat** to **Eat**.
```json
{
"States": [
{
"From": ["CallRat"],
"To": ["Eat"]
}
],
"Actions": [
{
"Type": "Inventory",
"Operation": "SetHotbar",
"Item": { "Compute": "FoodNPCItem" },
"Slot": 2,
"UseTarget": false
},
{
"Type": "Inventory",
"Operation": "EquipHotbar",
"Slot": 2,
"UseTarget": false
}
]
}
```
This isn't perfect, but it'll do for now and we can always come back and tweak things later. With that, we're done with both the idle and inter-NPC behaviours. Next we move on to combat and reacting to players!
--- Layout
Source: https://hytalemodding.dev/en/docs/official-documentation/custom-ui/layout
# Layout
The layout system determines how UI elements are positioned and sized on screen. Understanding layout is crucial for creating well-structured, responsive interfaces.
## Layout Fundamentals
Every UI element has:
* **Container Rectangle** - The space allocated by the parent
* **Anchor** - How the element positions/sizes itself within the container
* **Padding** - Inner spacing that affects child layout
* **LayoutMode** - How the element arranges its children (if it's a container)
```
┌─────────────────────────────────────┐
│ Container Rectangle (from parent) │
│ ┌───────────────────────────────┐ │
│ │ Anchored Rectangle │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Padding │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ Content Area │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
## Anchor
`Anchor` controls how an element positions and sizes itself within its container rectangle.
### Fixed Size
```
Button {
Anchor: (Width: 200, Height: 40);
}
```
Creates a 200×40 pixel button.
### Positioning
```
Label {
Anchor: (Top: 10, Left: 20, Width: 100, Height: 30);
}
```
* **Top**: 10 pixels from container's top edge
* **Left**: 20 pixels from container's left edge
* **Width**: 100 pixels wide
* **Height**: 30 pixels tall
### Anchoring to Edges
```
Button {
Anchor: (Bottom: 10, Right: 10, Width: 100, Height: 30);
}
```
Anchors the button to the bottom-right corner, 10 pixels from each edge.
### Stretching
```
Group {
Anchor: (Top: 0, Bottom: 0, Left: 0, Right: 0);
}
```
Stretches to fill the entire container (all edges at 0 pixels).
**Shorthand:**
```
Group {
Anchor: (Full: 0);
}
```
### Mixed Anchoring
```
Panel {
Anchor: (Top: 10, Bottom: 10, Left: 20, Width: 300);
}
```
* Fixed width of 300 pixels
* Stretches vertically between top and bottom edges
* 10 pixels from top and bottom
* 20 pixels from left
## Padding
`Padding` creates inner spacing, affecting where children are positioned.
### Uniform Padding
```
Group {
Padding: (Full: 20);
}
```
20 pixels of padding on all sides.
### Directional Padding
```
Group {
Padding: (Top: 10, Bottom: 20, Left: 15, Right: 15);
}
```
Different padding per edge.
### Shorthand
```
// Horizontal and vertical
Group {
Padding: (Horizontal: 20, Vertical: 10);
}
// Equivalent to:
// Top: 10, Bottom: 10, Left: 20, Right: 20
```
### Effect on Children
```
Group {
Anchor: (Width: 200, Height: 100);
Padding: (Full: 10);
Label {
Anchor: (Full: 0);
}
}
```
The label fills the group, but there's a 10-pixel gap on all sides due to padding.
```
┌──────────────────────┐
│ Group (200×100) │
│ ┌────────────────┐ │
│ │ Label │ │ ← 10px padding all around
│ │ │ │
│ └────────────────┘ │
└──────────────────────┘
```
## LayoutMode
`LayoutMode` determines how a container arranges its children.
### Top (Vertical Stack)
```
Group {
LayoutMode: Top;
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
}
```
Children stack vertically from top to bottom:
```
┌──────────┐
│ Button 1 │
├──────────┤
│ Button 2 │
├──────────┤
│ Button 3 │
└──────────┘
```
Each child's `Anchor.Bottom` adds spacing between elements:
```
Button { Anchor: (Height: 30, Bottom: 10); } // 10px gap after this button
```
### Bottom (Vertical Stack)
```
Group {
LayoutMode: Bottom;
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
}
```
Children stack vertically from top to bottom but aligned to the bottom edge of the parent:
```
┌──────────┐
│ Button 1 │
├──────────┤
│ Button 2 │
├──────────┤
│ Button 3 │
└──────────┘
```
### Left (Horizontal Stack)
```
Group {
LayoutMode: Left;
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
}
```
Children arrange horizontally from left to right:
```
┌────────┬────────┬────────┐
│ Button │ Button │ Button │
│ 1 │ 2 │ 3 │
└────────┴────────┴────────┘
```
Use `Anchor.Right` for spacing between elements.
### Right (Horizontal Stack)
```
Group {
LayoutMode: Right;
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
}
```
Children arrange horizontally from left to right but aligned to the right side of the parent:
```
┌────────┬────────┬────────┐
│ Button │ Button │ Button │
│ 1 │ 2 │ 3 │
└────────┴────────┴────────┘
```
### Center
```
Group {
LayoutMode: Center;
Group #Dialog {
Anchor: (Width: 400, Height: 300);
}
}
```
Centers the children horizontally.
### Middle
```
Group {
LayoutMode: Middle;
Group #Dialog {
Anchor: (Width: 400, Height: 300);
}
}
```
Centers the children vertically.
### CenterMiddle (Horizontal Stack, Fully Centered)
```
Group {
LayoutMode: CenterMiddle;
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
Button { Anchor: (Width: 80); }
}
```
Children stack horizontally from left to right, centered both horizontally and vertically within the parent:
```
┌────────────────────────────────────────────┐
│ │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ B1 │ │ B2 │ │ B3 │ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ │
└────────────────────────────────────────────┘
```
***
### MiddleCenter (Vertical Stack, Fully Centered)
```
Group {
LayoutMode: MiddleCenter;
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
Button { Anchor: (Height: 30); }
}
```
Children stack vertically from top to bottom, centered both horizontally and vertically within the parent:
```
┌────────────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ Button 1 │ │
│ ├──────────┤ │
│ │ Button 2 │ │
│ ├──────────┤ │
│ │ Button 3 │ │
│ └──────────┘ │
│ │
└────────────────────────────────────────────┘
```
***
### Full
```
Group {
LayoutMode: Full;
Label {
Anchor: (Top: 20, Left: 20, Width: 100, Height: 30);
}
}
```
Children use absolute positioning via their `Anchor` properties.
### TopScrolling
```
Group {
LayoutMode: TopScrolling;
ScrollbarStyle: $Common.@DefaultScrollbar;
// ... many children
}
```
Like `Top`, but adds a scrollbar if content exceeds container height.
Or use `BottomScrolling` for bottom-aligned content.
### LeftScrolling
```
Group {
LayoutMode: LeftScrolling;
ScrollbarStyle: $Common.@DefaultScrollbar;
// ... many children
}
```
Like `Left`, but adds a scrollbar for horizontal scrolling.
Or use `RightScrolling` for right-aligned content.
### LeftCenterWrap (Wrapping Horizontal Stack, Horizontally Centered)
```
Group {
LayoutMode: LeftCenterWrap;
Button { Anchor: (Width: 80, Height: 30); }
Button { Anchor: (Width: 80, Height: 30); }
Button { Anchor: (Width: 80, Height: 30); }
Button { Anchor: (Width: 80, Height: 30); }
Button { Anchor: (Width: 80, Height: 30); }
}
```
Children flow left to right. When there's no more horizontal space, they wrap to the next row. Each row is horizontally centered within the parent:
```
┌────────────────────────────────────────────┐
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ B1 │ │ B2 │ │ B3 │ │
│ └──────┘ └──────┘ └──────┘ │
│ ┌──────┐ ┌──────┐ │
│ │ B4 │ │ B5 │ │
│ └──────┘ └──────┘ │
│ │
└────────────────────────────────────────────┘
```
## FlexWeight
`FlexWeight` distributes remaining space among children.
```
Group {
LayoutMode: Left;
Anchor: (Width: 400);
Button {
Anchor: (Width: 100);
}
Group {
FlexWeight: 1; // Takes all remaining space
}
Button {
Anchor: (Width: 100);
}
}
```
Result:
* First button: 100px
* Middle group: 200px (400 - 100 - 100 = 200)
* Last button: 100px
### Multiple FlexWeights
```
Group {
LayoutMode: Left;
Anchor: (Width: 600);
Group { FlexWeight: 1; }
Group { FlexWeight: 2; }
Group { FlexWeight: 1; }
}
```
Remaining space (600px) is split:
* First group: 600 × (1/4) = 150px
* Second group: 600 × (2/4) = 300px
* Third group: 600 × (1/4) = 150px
## Visibility
```
Button #HiddenButton {
Visible: false;
}
```
**Effect:**
* Element and its children are not displayed
* Element is **not included in layout** (doesn't take up space)
--- Markup
Source: https://hytalemodding.dev/en/docs/official-documentation/custom-ui/markup
# Markup
## Markup
An element is the basic building block of a user interface. There are various types of elements with different properties, which are documented below.
Here's what the syntax looks like:
```
// Basic declaration of an element
// with its Anchor property set to attach on all sides of its parent
// with 10 pixels of margin
Group { Anchor: (Left: 10, Top: 10, Right: 10, Bottom: 10); }
Group { Anchor: (Full: 10); } // More concise version
// Declaration of a Label with a name
// (can be used to access the element from game code)
Label #MyLabel {
Style: LabelStyle(FontSize: 16); // or just Style: (FontSize: 16), type can be inferred.
Text: "Hi! I am text.";
}
// Declaration of a Group containing 2 children
// You can use FlexWeight to distribute any left-over space after explicit widths/heights have been subtracted
Group {
LayoutMode: Left;
Label { Text: "Child 1"; FlexWeight: 2; }
Label { Text: "Child 2"; FlexWeight: 1; }
}
```
### Documents
A UI document (*.ui* file) contains trees of elements. There can be multiple root elements.
### Named Expressions
Named expressions can be declared at any level of a document (including at the root) and will be scoped to that subtree, though they need to be declared at the top of the block.
```
// Example of named expressions, declared and used with @ prefix
@Title = "Hytale";
@ExtraSpacing = 5;
Label {
Text: @Title;
Style: (LetterSpacing: 2 + @ExtraSpacing);
}
```
You can use the spread operator `...` to reuse a named expression while overriding some of its fields:
```
@MyBaseStyle = LabelStyle(FontSize: 24, LetterSpacing: 2);
Label {
Style: (...@MyBaseStyle, FontSize: 36);
}
```
You can even layer multiple named expressions like so:
```
@TitleStyle = LabelStyle(FontSize: 24, HorizontalAlignment: Center);
@SpacedTextStyle = LabelStyle(LetterSpacing: 2);
Label {
Style: (...@BigTextStyle, ...@SpacedTextStyle);
}
```
#### Document references
A document can reference another document and access its named expressions.
```
// Document references are defined with $ prefix
$Common = "../Common.ui";
TextButton {
Style: $Common.@DefaultButtonStyle;
}
```
### Templates
You can declare a reusable bit of interface as a named expression and then instantiate it as many times as you want in the tree while customizing it.
You can override local named expressions as well as insert additional children at any point in the template tree. However, local named expressions must be defined at the very top of the block, before properties and child elements.
```
// This is the template
@Row = Group {
Anchor: (Height: 50);
Label #Label { Anchor: (Left: 0, Width: 100); Text: @LabelText; }
Group #Content { Anchor: (Left: 100); }
};
// Here we'll be using it twice in the document tree
Group #Rows {
LayoutMode: TopScrolling;
@Row #MyFirstRow {
@LabelText = "First row";
#Content { TextInput {} }
}
@Row #MySecondRow {
@LabelText = "Second row";
}
}
```
### Property Types
#### Basic Types
* **Boolean**
* `Visible: false;`
* `Visible: true;`
* **Int**
* `Height: 20;`
* **Float, Double, Decimal**
* `Min: 0.2;`
* **String**
* `Text: "Hi!";`
* **Char**
* `PasswordChar: "*";`
* *Note: The syntax is the same as a string, however a value of this type cannot contain more than one character!*
* **Color**
* `Background: #ffffff;`
* **Objects**
* `Style: (Background: #ffffff)`
* **Array**
* `TextSpans: [(Text: "Hi", IsBold: true)]`
#### Translations
Translation keys can be referenced anywhere in markup where you can provide a string, and they are then converted to a string when an element gets instantiated.
**Example:**
```
Label {
Text: %ui.general.cancel;
}
```
#### Colors
A color literal can be written as follows:
* `#rrggbb`: 6-digit hexadecimal representation for red, green, blue (fully opaque)
* `#rrggbb(a.a)`: 6-digit hexadecimal representation for red, green, blue with alpha between 0 and 1
* `#rrggbbaa`: 8-digit hexadecimal representation for red, green, blue and alpha
```
Group {
Background: #000000(0.3);
}
```
The `#rrggbb(a.a)` format is prefered over the `#rrggbbaa` format for
readability reasons.
#### Font Name
Font names can be referenced directly by their string ID. Internally this is using a custom type called `UIFontName`, however strings will be converted automatically to this type in markup.
```
Label {
Text: "Hi";
Style: (FontName: "Secondary");
}
```
**Available Font Names**
* *Default* - This is the default font that is used for most text and is always used unless a different option has been specified
* *Secondary* - This font is mostly used for headlines or elements that are supposed to stick out
* *Mono* - This is only used for development; For things such as profiling or error overlays.
#### Path
We can reference other UI assets with the `UIPath` type. Similar to `UIFontName`*,* Strings are automatically converted to a `UIPath`. Paths are always relative to the file where they have been declared in. Looking up *MyButton.png* in *Menu/MyAwesomeMenu.ui* will result in *Menu/MyButton.png*. If you want to reference a file that is in a parent folder you can go back one directory by writing two dots (*../MyButton.png*).
The markup syntax of a `UIPath` is the same as a String. (e.g. `Path: "Test.png"`)
Here are some more examples:
| UIPath | .ui File | Resulting Path |
| ------------------ | ------------------------------------- | ----------------- |
| MyButton.png | Menu/MyAwesomeMenu.ui | Menu/MyButton.png |
| ../MyButton.png | Menu/MyAwesomeMenu.ui | MyButton.png |
| ../../MyButton.png | Menu/Popup/Templates/MyAwesomeMenu.ui | Menu/MyButton.png |
#### Objects
Objects contain a set of properties. In this example we are assigning an Anchor object to the *Anchor* property:
```
Group {
Anchor: (
Height: 10,
Width: 20
);
}
```
You can find all objects that can be used in markup [here](type-documentation#elements).
## Visual Studio Code Extension
There is an official extension for Visual Studio Code which adds syntax highlighting for .ui files. You can find it [here](https://marketplace.visualstudio.com/items?itemName=HypixelStudiosCanadaInc.vscode-hytaleui).
--- Custom UI
Source: https://hytalemodding.dev/en/docs/official-documentation/custom-ui
# Custom UI
## What is Custom UI?
Custom UI is Hytale's framework for creating **custom user interfaces** controlled by the game server. Unlike the built-in Client UI (which is part of the game client and cannot be modified), Server UI allows you to create interactive screens and HUD overlays through Java plugins and asset packs.
As a modder, Custom UI gives you the power to:
* **Create custom interactive pages** - Shop interfaces, quest dialogs, server settings menus, admin panels
* **Add custom HUD overlays** - Quest trackers, status displays, custom health bars, server information
* **Design with markup** - Use `.ui` files to define reusable UI templates
* **Handle user interactions** - Respond to button clicks, form submissions, and other events
* **Localize your UI** - Support multiple languages using the game's translation system
## How Custom UI Fits Into Hytale's UI
Hytale's user interface is divided into two main categories:
### Client UI (Not Moddable)
Built-in interfaces controlled by the C# game client:
* Main menu and settings
* Character creation
* Built-in HUD (health, hotbar, chat)
* Inventory and crafting screens
* Development tools
**You cannot modify these** - they are part of the core game client.
### In-Game UI (Moddable via Server)
Server-controlled interfaces that you can create and customize:
#### Custom Pages
Full-screen interactive overlays that appear during gameplay:
* Can be dismissed by the player (ESC key)
* Capture all input (keyboard and mouse)
* Support loading states while waiting for server responses
* Perfect for: shops, dialogs, menus, configuration screens
#### Custom HUDs
Persistent overlay elements drawn on top of the game world:
* Display-only (no user interaction)
* Always visible during gameplay
* Lightweight and non-intrusive
* Perfect for: quest objectives, status indicators, server info panels
## Architecture Overview
Server UI uses a **command-based architecture**:
```
┌─────────────────────┐ ┌──────────────────────┐
│ Java Server │ │ C# Client │
│ (Your Plugin) │ │ (Game) │
├─────────────────────┤ ├──────────────────────┤
│ │ │ │
│ InteractiveCustomUI │ │ CustomPage or │
│ Page │ │ CustomHud │
│ ↓ build() ├────────→│ ↓ Apply │
│ UICommandBuilder │ │ Element Tree │
│ - append() │ │ ↓ Layout │
│ - set() │ │ Rendered UI │
│ - clear() │ │ │
│ │ │ │
│ ↓ handleDataEvent() │← Events │ User Interaction │
│ Process input │ │ (click, type, etc) │
│ sendUpdate() │ │ │
└─────────────────────┘ └──────────────────────┘
```
**The flow:**
1. Your Java code builds UI using `UICommandBuilder`
2. Commands are sent to the client as data
3. Client parses `.ui` markup files and creates visual elements
4. User interacts with the UI
5. Events are sent back to your Java code
6. You process events and send updates back
## Key Principles
### Declarative, Not Imperative
You don't create UI objects directly. Instead, you send **commands** that describe what you want:
* "Append this button template to that container"
* "Set this label's text to 'Hello World'"
* "Clear all children from this list"
### Asset-Driven
UI structure is defined in `.ui` markup files (assets), not hardcoded in Java. This enables:
* Designers to modify layouts without touching code
* Reusable UI components
* Consistent visual language
### Event-Driven
User interactions trigger events that flow back to your server code. You register event bindings and handle them in `handleDataEvent()`.
### Selector-Based
You target specific UI elements using **selectors:**
* `#MyButton` - Element with ID "MyButton"
* `#List[0]` - First child of element "List"
* `#List[0] #Title` - Element with ID "Title" in the first child of element "List"
* `#Label.TextColor` - The TextColor property of element "Label"
--- Common Styling
Source: https://hytalemodding.dev/en/docs/official-documentation/custom-ui/common-styling
# Common Styling
## Common UI Styling Library
This document describes the shared UI components and styles defined in `Common.ui`.
***
### Styles
We are providing some styles that can be re-used in any custom UI that, provide a cohesive UI experience with the core game UI. These styles are available in the `Common.ui` file.
You can find a list of all the styles defined with live examples by running the `/ui-gallery` command ingame.
### Importing
To use these styles in your UI file:
```
$Common = "Common.ui";
// Then reference styles and components:
$Common.@TextButton { @Text = "My Button"; }
$Common.@Container { ... }
```
The `Common.ui` file is at `Common/UI/Custom/Common.ui` within the Hytale pack. If your custom ui document is in a subfolder of `Common/UI/Custom/`, reference it via relative path traversal:
```
$Common = "../Common.ui";
```
You can find more info about paths [here](markup#path).
--- The Hytale Client
Source: https://hytalemodding.dev/en/docs/established-information/client
The client is written in C#, compiled to machine code before distribution, and not moddable. Single-player runs on a local server. All mods must go through the server API.
# No Client-Side Mods
There is no client-side modding.
> We don't intend to support any client mods - Slikey
## Why?
1. No need to download externally.
* Hytale wants to "avoid a fragmented ecosystem where every server requires a different modded client".
2. Client security.
* Running `.jar` files from third-party sites on the client can easily introduce malware.
> \[We don't want to expose players] to the security and safety risks that exist on some other modding platform.
3. Client stability.
* Hytale may move to other platforms on which client modding is impossible, so they're locking it down early to set the right direction early.
> We believe that the results that people get in other block games using client mods can be easily achieved using our server-sided modding. In other block games you have to install a client mod in order to get new blocks, NPCs, UI, etc.Hytale does all of that just by listening to the server. The server will basically "mod" the client temporarily in a really safe way to add and modify your gameplay experience. You could make your very own version of Orbis, using none of our models, music, world generation, combat or anything. You have control and you can rip it all out without ever touching the client. Think about how Roblox works.. You never change your App. You just click on an experience you want and you get all you need without modding your installation itself. - Slikey
## Client Mods are Not Needed
The typical use cases for client mods are:
* Cosmetic changes (resource packs, shaders, capes, etc.): servers own visuals, not the client.
* Performance: the base game will offer necessary performance controls.
* Accessibility: the base game will be accessible on its own.
* User Interface: servers own UI, not the client.
### Client-Side Prediction
Hytale uses data-driven client-side prediction to mitigate lag, as all interactions take place on the server and are relayed back to the client. This is already present for some systems but currently limited, though further integration is planned for the future. It's also planned to work with the visual scripting system in the future for further optimization.
## Client-Side Execution
The team is exploring allowing execution of code by the client (which is distinct from modding the client; this is code sent from the server), though this is not guaranteed.
# Models
The client and assets pipeline accepts `.blockymodel`, a custom format for Hytale. The official Blockbench plugin will allow you to export to this format.
# Client Technical Details
There will only ever be one current, supported version of the client.
> Java version will be kept updated by us and right now we are on the latest version (Java 25)
> The game version will not be selectable to avoid the version jungle we experienced on our hypixel server network. We simply want to make sure that multiplayer networks can rely on players having the latest version of the game to not have to support 15+ versions of the client. - Slikey
The client is multithreaded.
## GUI
The GUI is built from C# bindings using Noesis.
## Build System
Hytale uses NativeAOT ("Ahead of Time") to build the client, which compiles C# to machine code for distribution. This means that, without reverse-engineering the machine code, mods will have to go through the server API.
# Other Information
The client has its own versioning and protocol to interact with servers.
--- Server-First Development
Source: https://hytalemodding.dev/en/docs/established-information/server/server-first-dev
Hytale is being developed with a "server-first" approach. This means that the server is considered the authoritative source of game state and logic, while the client primarily handles rendering and user input.
## What does "Server-First" mean?
In a server-first architecture, the server is responsible for managing the game world, enforcing rules, and processing player actions. The client connects to the server to receive updates about the game state and to send player inputs. This approach helps ensure consistency and fairness in multiplayer environments, as the server has control over the game logic.
## Benefits of Server-First Development
* **Consistency**: Since the server is the authoritative source, all players experience the same game state, reducing discrepancies and cheating.
* **Scalability**: Server-first architectures can be more easily scaled to accommodate large numbers
of players, as the server can manage resources and distribute load effectively.
* **Security**: By centralizing game logic on the server, it becomes more difficult for players to manipulate the game state unfairly.
* **Easier Updates**: Updates and changes can be made on the server side without requiring players to download new client versions.
* **Only One Version**: There won't be a version selector for the Hytale Client, meaning that server owners do not need to support multiple versions and can rely on everyone being on the latest client. This will help both server owners and modders.
## Implications for Modding
Modders will primarily interact with the server-side components of Hytale. This means that mods will often focus on altering game logic, rules, and behaviors that are managed by the server. Client-side modifications are not allowed (as of the information given right now).
Overall, the server-first approach in Hytale aims to create a robust and fair multiplayer experience while providing modders with powerful tools to customize and enhance the game.
## Conclusion
The server-first development model is a key aspect of Hytale's architecture, ensuring a consistent and secure multiplayer experience. Modders should focus on server-side modifications to fully leverage the capabilities of this approach and create engaging content for players.
--- Creative
Source: https://hytalemodding.dev/en/docs/established-information/gameplay/creative
# Creative Mode
Creative mode in Hytale is a sandbox mode that allows players to edit the world freely.
## World Editing Tools
Creative mode includes a variety of tools to help players edit the world. It includes undo functionality for edits.
Note that the below sections were unnamed in the video demo of the tools.
***
### Laser Pointer
Displays a ray from the player's camera in the direction the player is looking.
#### Settings
* Duration (Integer Slider): Description unknown as of December 2025.
* Laser Color (Hex Color Code): Description unknown as of December 2025.
### Sculpt Brush Tool
Allows the user to raise and lower the terrain to more accurately sculpt and blend shapes. Supports most features of the Paint Brush.
### Line Brush Tool
Unknown as of December 2025.
### Selection Tool
Allows the user to select and edit regions of the world by transforming selections of blocks or a prefab via position, rotation, and extrusion.
#### Settings
* Mode (Combo Box: Set, Wall, Fill, Replace): Description unknown as of December 2025.
* Material (Integer Slider): Description unknown as of December 2025.
* Weight (Integer Slider): Description unknown as of December 2025.
* Pitch (Integer): Description unknown as of December 2025.
### Ruler
Allows the user to measure distances between multiple points in 3D space, with support for multiple measurement lines and editing of measured points.
### Layer Brush Tool
Unknown as of December 2025.
### Paste Tool
Allows the user to paste a copied selection of blocks or a prefab at the point at which the player is looking. The paste is relative to the selection's anchor point. If the selection is a prefab, the anchor point may be set in the Prefab Editor. It is unknown if the selection's anchor may be set otherwise.
### Flood Brush Tool
Unknown as of December 2025.
### Paint Brush Tool
Brushes are used to make large changes to the world from far away.
#### General Abilities
* Place Shapes and Prefabs
* Replace Blocks
* Create Axis Locks and Masks
#### Settings
* Material
* Width (Integer Slider): Description unknown as of December 2025.
* Height (Integer Slider): Description unknown as of December 2025.
* Shape (Combo Box: Cube, Sphere, Cylinder, Cone, Inverted Cone, Pyramid, Inverted Pyramid, Cuboid Grid): Description unknown as of December 2025.
* Origin (Combo Box: Center, Bottom, Top): Description unknown as of December 2025.
* Mirror Axis (Combo Box: None, Unknown other options): Description unknown as of December 2025.
* Wall Thickness (Integer Slider): Description unknown as of December 2025.
* Density (Integer Slider): Description unknown as of December 2025.
#### Presets:
* Boulder: Creates a boulder at the point at which the player is looking.
* Grassy Path: Transforms the terrain into a grass path at the point at which the player is looking.
* Rubble: Generate rubble at the point at which the player is looking.
* Tentacle: TBD
* Lava Cracks: Transforms the terrain into a lava crack at the point at which the player is looking.
* Hot Springs: TBD
* Ruins: Generates a ruin at the point at which the player is looking.
* Ruin Roofs: Generates a ruin with a roof at the point at which the player is looking.
* Grass: TBD
* Mountain: Places consecutive chunks of terrain to form a mountain at the point at which the player is looking.
* Ruins: TBD
***
### Entity Grabber Tool
Unknown as of December 2025.
### Noise Brush Tool
Unknown as of December 2025.
### Scatter Brush Tool
Unknown as of December 2025.
### Extrude Brush Tool
Unknown as of December 2025.
### Prefab Selector Tool
Allows the user to select a prefab when inside the Prefab Editor. May be used with the copy command.
### Smooth Brush Tool
Unknown as of December 2025.
### Hitbox Tool
Unknown as of December 2025.
### Set Anchor Tool
Allows the user to set an anchor point for a prefab when inside the Prefab Editor.
### Tint Brush Tool
Unknown as of December 2025.
***
## Creative Mode Quick Settings
Note that these are WIP and subject to change. **Not all settings and functionality are currently known**.
* Tool Reach Distance (Integer Slider): Description unknown as of December 2025.
* Tool Delay Minimum (Integer Slider): Description unknown as of December 2025.
* Brush Opacity (Integer Slider): Description unknown as of December 2025.
* Selection Opacity (Integer Slider): Description unknown as of December 2025.
* Selection Panel Opacity (Integer Slider): Description unknown as of December 2025.
* Enable Brush Shape Rendering (Toggle): Description unknown as of December 2025.
* Display Legend (Toggle): Description unknown as of December 2025.
* Show Tool Notifications (Toggle): Description unknown as of December 2025.
* Default Laser Pointer (Hex Color Code): Description unknown as of December 2025.
* Enable Brush Spacing (Toggle): Description unknown as of December 2025.
* Spacing Between Br? (Integer Slider): Description unknown as of December 2025.
* Flight Intertia (Integer Slider): Description unknown as of December 2025.
* Flight Mode (Combo Box: Hover, Directional): Description unknown as of December 2025.
* No Clip (Toggle): Description unknown as of December 2025.
* Air Placement (Toggle): Description unknown as of December 2025.
## Content Creation Tools
Hytale includes a variety of built-in content creation tools to help players create and edit content.
### Asset Editor
Unknown as of December 2025.
### Model Editor
Unknown as of December 2025.
### Asset Node Editor
Unknown as of December 2025.
### World Tools
These are tools that allow the user to directly edit the current world.
#### Prefab Editor
Allows the user to create or edit prefabs in a separate space.
##### Settings
* Saved configs (Unknown type; presumably a file)
* Root Dir (Unknown type; presumably a folder)
* Prefab Paths or Folders (Unknown type; presumably a list of paths or folders)
* Recursively Search for Prefabs (Toggle)
* Spawn Entities and NPCs (Toggle)
* Desired Y Level to Paste (Integer)
* Blocks Between Prefabs (Integer)
* WorldGen Type (Combo Box: Flat, Unknown other options): Description unknown as of December 2025.
* (Flat WorldGen Only) Num Air Blocks Below Prefabs (Integer): Description unknown as of December 2025.
* Axis to Paste On (Combo Box: X, Unknown other options): Description unknown as of December 2025.
* Alignment Method (Combo Box: By Anchor, Unknown other options): Description unknown as of December 2025.
#### Prefab List
* Lists all user created prefabs and allows the user to load a prefab into the Paste tool.
* User can pick from server, assets, or worldgen prefabs.
#### Spawn NPC
Unknown as of December 2025.
#### Spawn Particle
Unknown as of December 2025.
#### Change Player Model
* Allows the player to change their model with an adjustable scale.
#### Tint Chunk
Unknown as of December 2025.
#### Instances
Unknown as of December 2025.
### Builder Tools
Unknown as of December 2025.
### Machinima
Machinima is a built-in sequence editor, allowing the user to create and edit sequences of actions to be played back in game. It includes a built-in interactive keyframe editor.
The speed of playback can be adjusted via commands.
The user may add actors for the sequence to control.
* Reference
* Camera
* Entity
* Item
* Player
## Commands
Commands are entered into the world's chat console and begin with a forward slash `/`.
* `set `: allows the user to set the blocks in a selection to the specified block.
* `inventory clear`: allows the user to clear their inventory.
--- NPC Roles
Source: https://hytalemodding.dev/en/docs/guides/npc-workings/npc-role
# Describing your role
> *All the world's a stage, and all the men and women merely players: they have their exits and their entrances; and one man in his time plays many parts, his acts being seven ages.*
>
> **- Shakespeare (As You Like It)**
Roles make up the core of a NPC in how it interacts with the world. It defines things like appearance, max health, equipped items, what it drops upon death, etc. at its most basic functionality. Think of this as defining how it looks and the statistics it has.
Roles can be changed during the lifetime of an NPC, so the possibilities are well, not endless, but many in how your NPC becomes unique.
## Types
Roles come in three flavors: **Generic**, **Variant** & **Abstract**.
Out of all three is **Generic** the default one. It is declared as is with no preprocessing needing to be done. What is preprocessing you wonder? In short it looks at the JSON file it is and see if anything needs to be changed before Hytale reads it for its data.
It is rarely used outside of testing because **Templates** are a widely used practice, because it saves time and let you reuse data and instructions across several roles with just a simple reference. The most reused templates can be found in the `Server\NPC\Roles\_Core\Templates` folder.
**Variant** and **Abstract** are tied together because one type relies on the other in order to do its preprocessing magic. The short version is that the **Variant** role copies the data from the **Abstract** role and then overwrite it withs own on top of that. A example will help visualize it.
> **Template\_Shape.json file**
```json
{
"Type":"Abstract",
"Appearance": { "Compute": "Appearance" },
"MaxHealth": { "Compute": "MaxHealth" },
"KnockbackScale": 0.5,
"Parameters":{
"Appearance":{
"Value":"Point",
"Description":"Model to be used"
},
"MaxHealth":{
"Value": 10,
"Description":"Max health for the NPC"
}
}
}
```
> **Cube.json file**
```json
{
"Type":"Variant",
"Reference":"Template_Shape",
"Modify":{
"Appearance": "Cube",
"MaxHealth": 30
}
}
```
Variant **Cube** inherits from the Abstract **Template\_Shape** and creates JSON file that looks like this in memory after all preprocessing is done:
> **Cube role JSON in memory**
```json
{
"Type":"Variant",
"Reference":"Template_Shape",
"Appearance": "Cube",
"MaxHealth":30,
"KnockbackScale": 0.5
}
```
While this example has a incomplete role it helps to illustrate how role **Types** work for roles at a high level.
## Parameters and modifying them
You may noted that in the previous example was the fields **Parameters** and **Modify**. They are used with the preprocessor to help to both make **Abstract** roles modifiable in their **Variant** roles, they are also used in other contexts when it come to making NPCs. Such as passing data to **Components** (Not to be confused with components in the **ECS**)
We can set the parameters easily in our role JSON file.
```json
"Parameters":{
"ParameterName1":{
"Value":"Apples",
"Description":"Put a short description that describes this parameter well. In this case a string."
},
"ParameterName2":{
"Value": 15,
"Description":"Numbers work well, integers and floating points are all good."
},
"ParameterName3":{
"Value": ["Food_Pie_Apple", "Food_Kebab_Vegetable"],
"Description":"You can store arrays of strings."
}
}
```
Also easily modify them in another role JSON file that references our template:
```json
"Modify": {
"ParameterName1": "Oranges",
"ParameterName2": 42.67,
"ParameterName3": ["Rock_Stone"]
}
```
## Compute or more preprocessing
This **Compute** thing has appeared several times now but never truly been addressed, but it will be now.
```json
{ "Compute": "ParameterName" }
```
This wonderful tool can be used for more than just modifying parameters in a Role, in fact its vital when creating **Instructions** in order to ensure that you can correctly pass data to them in a readable way. Instead of using [magic numbers](https://en.wikipedia.org/wiki/Magic_number_\(programming\)) you can just pass the parameter through a **Compute** preprocessor.
At its most basic level all it does is pass a value from your **Parameters** but it can do much more. Like math for instance.
```json
"Parameters":{
"First":{
"Value":2,
"Description":"First number."
},
"Second":{
"Value": 3,
"Description":"Second number."
}
},
"MaxHealth": { "Compute": "First + Second"}
```
It supports the most common mathematical operations like addition, subtraction, multiplication and division.
* `+` : Addition **Example:** left + right
* `-` : Subtraction **Example:** left - right
* `*` : Multiplication **Example:** left \* right
* `/` : Division **Example:** left / right
**Compute** also supports more advanced operations like negating values and exponents:
* `-` : Negates the numeric value after it. **Example:** 10 + -10 = 0
* `E` : Scientific notation for large numbers. **Example:** 5**E**5 = 500,000
* `%` : Remainder, also known as modulus. **Example:** 35 % 32 = 3
* `**` : Exponent like 10² which would give you 100. **Example:** 10\*\*2 = 100
You can also use open brackets like `( )` to make more complicated computation. **Example:** MaxHealth + (MaxHealth / 2)
Square brackets `[ ]` are used for making arrays. Like string and numeric arrays in **Compute**. But the usage is quite limited at the moment.
* **\[0, 1, 2]** : Numeric array
* **\["A", "B", "C"]** : String array
Comparing things can also be done. Useful for setting a value in the **"Enabled"** field in a **Instruction** for example.
* `==` : Equals, if the left and right side are equal it is **true**. **Example:** left == right
* `!=` : Not equal, if the left and right side are not equal it is **true**. **Example:** left != right
* `>` : Greater than, if the left side is greater than the right side it is **true**. **Example:** left > right
* `>=` : Greater than or equals, if the left side is greater than or equals to the right side it is **true**. **Example:** left >= right
* `<` : Lesser than, if the left side is lesser than the right side it is **true**. **Example:** left \< right
* `<=` : Lesser than or equals, if the left side is lesser than or equals to the right side it is **true**. **Example:** left \<= right
There are other functions with **Compute** too that Hytale added in order to make their JSON preprocessing much easier. Here are all of them found so far:
* **true** : Boolean constant for true.
* **false** : Boolean constant for false.
* **PI** : The value of [pi](https://en.wikipedia.org/wiki/Pi).
* **max**( *first, second* ) : Puts the biggest of the two numbers in the **Compute**.
* **min**( *first, second* ) : Puts the smallest of the two numbers in the **Compute**.
* **isEmpty**( *argument* ) : Checks if the provided string or **Parameter** is empty.
* **isEmptyStringArray**( *argument* ) : Checks if the provided string array or **Parameter** is empty.
* **isEmptyNumberArray**( *argument* ) : Checks if the provided number array or **Parameter** is empty.
* **random**() : Provides a random number between 0.0 and 1.0.
* **randomInRange**( *first, second* ) : Provides a random number between *first* and *second* arguments.
* **makeRange**( *argument* ) : Makes a number array with two values that are the same as the *argument*.
**Note:** It is possible to extend the **Compute** functions in other ways but this won't be addressed here.
## Motion controllers
Motion controllers tell the NPC how to move around in its **Role**. Roles that walk on the ground use the **Walk** controller. A flying **Role** use the **Fly** controller. Meanwhile aquatic **Role**'s use the **Dive** controller.
The motion controllers are used in conjunction with **"BodyMotion"** inside instructions in order to make the NPCs move around. But keep in mind that they are still affected by gravity and other external forces even without input from the instructions.
In its current implementation in Hytale are multiplie motion controllers ill-advised because of how buggy it is. Even reference in the `Template_Birds_Passive.json` file that they need to hack it in order to make flying roles walk on the ground.
> **Example from Template\_Kweebec\_Sapling.json**
```json
"MotionControllerList": [
{
"Type": "Walk",
"MaxWalkSpeed": { "Compute": "MaxSpeed" },
"Gravity": 10,
"RunThreshold": 0.3,
"MaxFallSpeed": 15,
"MaxRotationSpeed": 360,
"Acceleration": 10
}
]
```
## Instructions
With the skeleton of a **Role** being explained we now come to the meat of it. The beating *heart* of a NPC that let it do truly wondrous things.
As was said in the introduction, are instructions executed from top to bottom. Stopping at the first deepest nested instruction whose **Sensor** that match.
> **Taken from the introduction page**
```json
"Instructions":[
{
"Sensor":{
"Type":"State",
"State":"Idle"
},
"Instructions":[
{
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"BodyMotion":{
"Type":"Seek",
"RelativeSpeed": 0.6,
"StopDistance":3,
}
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Wander"
}
]
}
]
```
This is an example of a **2 deep** nested instructions. It goes from the **"Instructions"** from the role and then iterates through its only instruction it has. If the **State** of the NPC is `Idle` it ***matches*** and will try to either execute **Actions** or **Instructions**. Never both at the same time.
Assuming that the NPC **State** is `Idle` it ***matches*** and will iterate through the instructions from that instruction.
> **First match**
```json
"Instructions":[
{
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"BodyMotion":{
"Type":"Seek",
"RelativeSpeed": 0.6,
"StopDistance":3,
}
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Wander"
}
]
```
It looks for a Player which holds red berries in hand but it can't find any in the first instruction which makes it NOT ***match***. So it tries the next one which is a reference to a **Component** that makes the NPC wander around in a idle manner.
> **Final match**
```json
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Wander"
}
```
If nothing changes the NPC will continue executing this instruction until the first instruction matches.
### Continue Vs. TreeMode
The main difference between `"Continue"` and `"TreeMode"` in a instruction is how it handles finding matches.
With `"Continue"` set to **true** the instruction executing it will continue executing instructions despite finding a match in this instruction. It is useful for running logic in the background and perform reusable logic in a `Component`.
With `"TreeMode"` set to **true** the instruction executing it will continue after all instructions inside this instruction fail to match. Confusing? Thought so. It acts like a behavior tree where it will lock onto the first matching instruction and keep executing it until it no longer matches.
If you set `"InvertTreeModeResult"` to **true** in a instruction nested inside the `"TreeMode"` instruction will invert its behavior.
> **Example tree**
```json
"Instructions":[
{
"TreeMode":true,
"Instructions":[
{
"Sensor":{
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Hearts",
"TargetNodeName": "Head"
}
]
},
{
"Sensor":{
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Food_Pie_Apple"]
}
]
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Stunned",
"TargetNodeName": "Head"
}
]
}
]
},
{
"Sensor":{
"Type":"Any"
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Question",
"TargetNodeName": "Head"
}
]
}
]
```
### Template\_Livestock.json is your bible
Why should you care about this file so much? It is because it contains all the *tricks* and good instruction *patterns* that you will need to make interesting NPCs with your **Roles**.
It is located in: `Server\NPC\Roles\_Core\Templates`
For example if you look at the `"InteractionInstructions"` in the file will you see a reusable tree structure for handling different cases where the player interacts with the NPC. It is easy to modify and get to work in your own Role JSON files.
It also shows a good usage of the `"Enabled"` property in instructions which leads to highly modular templates that can be reused for ***many*** **Roles**.
**Note:** For programmers is the `NPCPlugin` class your bible ***and*** documentation for NPCs. **Class path:** `com.hypixel.hytale.server.npc.NPCPlugin`
### Main instructions
In the **Role** `"Instructions"` is where the NPC truly comes alive. It is run every in-game tick where it executes instructions as far as it can.
This is a good place to put "scripts" outside states which is run every tick. Let you reset things like *Alarms* and do **Flock** logic.
Using our previous example we can make our NPC emit hearts every 2 to 3 seconds.
To do that we first need to setup a **Timer** in our instructions. To do that we first make a instruction with a `Any` **Sensor** that is set to run only **Once**. Make sure to set `"Continue"` to **true** in it to ensure instructions after it can execute despite this always matching.
After that we implement our second instruction which checks if the **Timer** that we started has stopped with the `Timer` **Sensor**. It matches if the **Timer** has stopped and will execute its actions that spawns heart particles atop the NPCs head and restarts the timer.
> **Modified example from the introduction**
```json
"Parameters":{
"HeartTimer":{
"Value":"HeartEmitterTimer",
"Description":"The name for the timer."
}
},
"Instructions":[
{
"$Comment":"Initialize our Timer.",
"Continue":true,
"Sensor":{
"Type":"Any",
"Once":true
},
"Actions":[
{
"Type": "TimerStart",
"Name": { "Compute": "HeartTimer" },
"StartValueRange": [0.1, 0.1],
"RestartValueRange": [2, 3]
}
]
},
{
"$Comment":"Let us check if our heart timer is finished.",
"Continue":true,
"Sensor":{
"Type":"Timer",
"Name": { "Compute": "HeartTimer" },
"State":"Stopped"
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Hearts",
"TargetNodeName": "Head"
},
{
"Type":"TimerRestart",
"Name": { "Compute": "HeartTimer" }
}
]
},
{
"Sensor":{
"Type":"State",
"State":"Idle"
},
"Instructions":[
{
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"BodyMotion":{
"Type":"Seek",
"RelativeSpeed": 0.6,
"StopDistance":3,
}
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Wander"
}
]
}
]
```
### Interaction instructions
In the **Role** `"InteractionInstruction"` is where it control how the NPC can interact with players. The interaction distance is generous so you can limit it by checking how far the matching player is.
The theory is that you first run a check whether the player can interact with it, if it can't you set it as not interactable. If you can interact with the NPC then you set a hint message and make it interactable.
Once it detects activation you run whatever actions you desire.
You can find a reference in the `Kweebec_Merchant.json` for how it works.
> **Simple interaction logic**
* Check if NPC is not interactable.
* Set as interactable with hint.
* Execute actions upon interaction.
For more advanced interaction logic consult the `Template_Livestock.json` file. It utilizes `"TreeMode"` on each scenario set to **true** to perform many interaction checks before finally setting the NPC as not interactable.
> **Advanced interaction logic**
* For each instruction with `"TreeMode"` set to to **true**.
* Check if NPC is interactable.
* Set as interactable with hint.
* Execute actions upon interaction.
* Set as not interactable.
**Note:** It can't override **blocking** actions in the main `"Instructions"`.
### Death instruction
In the **Role** `"DeathInstruction"` is where a single instruction will be run upon NPC death. It should be kept short to tie up any loose ends or do something as silly as spawn a mouse upon death. Otherwise it works like a normal instruction where it can have nested instructions.
## Good practices
Use **Parameters** when possible in the **Role** file. Can be ignored for single use cases where the same piece of data won't appear again.
Use **Compute** wherever possible for both readability and being able to easily change parameters from one place while testing your NPC **Role**.
Try to use *cheap* **Sensors** when possible to avoid lagging the server.
Learn and understand the versatility of **States** because that is the main building block of your NPC **Role**. This is a topic for another page in itself.
When you repeat the same piece of instructions, actions or sensors many times it may be worth looking into **Component** JSON files. But that is a topic for another page.
## What now?
Test a lot. Figure out what makes a NPC tick through its role. Look at existing roles made by the Hytale developers to see how they did it.
Explore how combat works with the interaction system.
--- NPC States
Source: https://hytalemodding.dev/en/docs/guides/npc-workings/npc-states
# States
You are in a state now even if you don't know it. You are now `Reading` this right? While reading this you occasionally take a `.Sip` from your trusty cup of coffee.
What does this have to do with **States** you may wonder? Like your life a NPC goes through various states during its lifecycle in Hytale.
## What is a state?
In short is a **State** a way of keeping track of what a NPC is doing without requiring setting and reading numbers, instead Hytale does all that *work* for you by translating [human readable](https://en.wikipedia.org/wiki/Human-readable_medium_and_data) text into numbers. This creates a highly efficient system that is a lot less taxing for the game engine to deal with.
The practical usage of **States** is that it makes writing and reading the **Instructions** of a NPC easier. The core of a **State** starts by placing down a **Sensor** with the name of it.
```json
"Sensor":{
"Type":"State",
"State":"Idle"
}
```
You can make as many **States** as you want but ***REMEMBER*** they all must be **LINKED** together in some way from the **StartState** state. Otherwise your **Role** fails to validate in your JSON file and you will become very sad.
Using the example above this **State** will work if you set `"StartState"` to `"Idle"` in your **Role**. If its not set the default state will be: `"start"`, so make sure to set it to a state that makes sense like `"Idle"`.
```json
{
"StartState":"Idle",
"Instructions":[
{
"Instructions":[
{
"Sensor":{
"Type":"State",
"State":"Idle"
},
"Actions":[
{
"Type":"Nothing"
}
]
}
]
}
]
}
```
How does this work? Let's map this to a table with **State transitions**.
| Source | From | To | Trigger |
| :------: | :------------: | :----: | --------- |
| **Role** | `"StartState"` | `Idle` | **Start** |
This is valid because the `Idle` is referenced by the `"StartState"` from the **Role**, thus creating a link. Remember there cannot be any *stray* states.
Its great and all, but it lacks transitions to other states. Here is another example with valid transitions between states.
```json
{
"StartState":"Idle",
"Instructions":[
{
"Instructions":[
{
"Sensor":{
"Type":"State",
"State":"Idle"
},
"Instructions":[
{
"Sensor":{
"Type": "Player",
"Range": { "Compute": "ViewRange" },
"Filters": [
{
"Type": "LineOfSight"
},
{
"Type": "ViewSector",
"ViewSector": 180
},
{
"Type": "ItemInHand",
"Items": ["Rock_Stone"]
}
]
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Hearts_Subtle"
},
{
"Type":"State",
"State":"Work"
}
]
}
]
},
{
"Sensor":{
"Type":"State",
"State":"Work"
},
"Instructions":[
{
"Sensor":{
"Type": "Player",
"Range": { "Compute": "ViewRange" },
"Filters": [
{
"Type": "LineOfSight"
},
{
"Type": "ViewSector",
"ViewSector": 180
},
{
"Type": "ItemInHand",
"Items": ["Rock_Stone_Cobble"]
}
]
},
"Actions":[
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Alerted"
},
{
"Type":"State",
"State":"Idle"
}
]
}
]
}
]
}
]
}
```
The state transitions will look like this with this:
| Source | From | To | Trigger |
| :---------: | :------------: | :----: | --------------------------------------------- |
| **Role** | `"StartState"` | `Idle` | **Start** |
| Instruction | `Idle` | `Work` | Player with item in hand: Rock\_Stone |
| Instruction | `Work` | `Idle` | Player with item in hand: Rock\_Stone\_Cobble |
As you can see from these transitions this makes a complete chain of states where no state is hanging loose. You may have noticed that we use a **Action** of the type `"State"` there, that is how we switch between states in NPCs.
```json
{
"Type":"State",
"State":"Work"
}
```
The **Sensor** and **Action** `"State"` are interlinked, and Hytale makes sure of that. A lot goes on in the background to ensure NPCs will not infinitely get stuck in a **State**. Hytale outright refuses you to add or update **Roles** if **States** aren't linked.
## Sub-states
The **Sub-state** is like a **State** but its a smaller part of one. By default you enter one even if you haven't defined it. For example if you just go into the **State** of `"Idle"` without defining its **Sub-state** you in fact enter the `"Idle.Default"` **State**. Its more of a convenience feature instead of having to type it in every time you just want to enter the `"Idle"` **State**.
But the main feature of **Sub-states** is that it let you partition states into different tasks you want a NPC to perform. For example you can make a Job driver where the NPC does work and it switches states until it exits it and goes back to being `"Idle"`.
Within a **State** can you set or check what **Sub-state** it has by simply putting `.SubStateName` as its `State`. It is the `.` in the name that determines that it's a `Sub-state` its setting or checking.
> **Checking for sub-state**
```json
{
"Sensor":{
"Type":"State",
"State":".SubStateName"
}
}
```
> **Setting a sub-state**
```json
{
"Actions":[
{
"Type":"State",
"State":".SubStateName"
}
]
}
```
*In the example below the details will be omitted for clarity.*
```json
{
"StartState":"Idle",
"Instructions":[
{
"Instructions":[
{
"$Comment":"Timer initialization.",
"Sensor":{
"Type":"Any",
"Once":true
}
},
{
"$Comment":"Work timer is triggered and we will check if we can do farm work. If we can go to the Farm state.",
"Continue":true,
"Actions":[
{
"Type":"State",
"State":"Farm.Goto"
}
]
},
{
"Sensor":{
"Type":"State",
"State":"Idle"
}
},
{
"$Comment":"Main state of 'Farm' where our logic lies in.",
"Sensor":{
"Type":"State",
"State":"Farm.Goto"
},
"Instructions":[
{
"$Comment":"Perform checks to see if our Farming state fails, and if it does go back to being Idle.",
"Continue":true
},
{
"$Comment":"Go to our designated farming area.",
"Sensor":{
"Type":"State",
"State":".Goto"
},
"Instructions":[
{
"$Comment":"Reached our destination. Switch to '.Jobdriver' sub-state.",
"Actions":[
{
"Type":"State",
"State":".Jobdriver"
}
]
}
]
},
{
"$Comment":"Perform farming job until we no longe can.",
"Sensor":{
"Type":"State",
"State":".Jobdriver"
}
}
]
}
]
}
]
}
```
A example transition table could look this:
| Source | From | To | Trigger |
| :---------: | :------------: | :--------------: | -------------------------------------------------- |
| **Role** | `"StartState"` | `Idle` | **Start** |
| Instruction | `Idle` | `Farm.Goto` | Timer for checking Work triggered and we can farm. |
| Instruction | `Farm.Goto` | `Farm.Jobdriver` | NPC reached target destination. |
| Instruction | `Farm` | `Idle` | No more farming work to be done. |
Notice how we just went to the `Farm.Goto` **State** immediatly instead of first going to the `Farm` **State**? That is a feature of **Sub-states**. It let you do general logic inside a **State** while doing more fine-grained logic inside a **Sub-state**.
With clever use of **Sub-states** will writing **Instructions** be a lot more enjoyable.
**Note:** `"$Comment"` will be ignored by Hytale when reading the JSON file. Its used for commenting instructions and other things. There is also `"$Todo"` for the same purpose.
## Parent state and JSON components
If you ever looked into Hytale **Component** files then you will notice they don't use `"State"` **Actions** when setting state. That is because **Components** are special cases. You will need to instead use the type `"ParentState"` to set a **State** from a **Component**.
> **Example from: Component\_Instruction\_Damage\_Check.json**
```json
"Parameters": {
"_ImportStates": [ "Chase", "Search" ]
}
```
```json
{
"Type": "ParentState",
"State": "Search"
}
```
The technical bits is that it uses the `"_ImportStates"` parameter in the **Component** parameters. The actual **State** names you define in the `"_ImportStates"` parameter are aliases or stand-ins for the actual **States** being passed in from the ***outside***. That way you can reuse the **Component** in many **Roles** which all can have different **State** names.
In order to input the stand-in states for the real states you need to **Modify** them by setting the `"_ExportStates"` in the **Component** you **Reference** to inside the **Instruction\Action\Sensor**. You need to to input the **States** in the same order as the `"_ImportStates"` define it in the **Component** in order for it to work. The example below helps illustrate how it works.
> **Example from: Template\_Livestock.json**
```json
{
"Reference": "Component_Instruction_Damage_Check",
"Modify": {
"_ExportStates": [ "Flee.Switch", "Panic" ],
"AlertedRange": { "Compute": "AlertedRange" }
}
}
```
## State transitions
In a **Role** can you peform **Actions** between **States**. It is useful for doing things like clearing animations or other utilitarian tasks. CPU intensive **Actions** should be frowned upon because switching **States** may happen fast.
The `"StateTransitions"` property contains conditions and **Actions** to perform *between* **State** transitions.
> **Example from: Template\_Livestock.json**
```json
{
"StateTransitions":[
{
"$Comment": "Always clear target and reset instructions when going back to idle.",
"States": [
{
"To": [ "Idle" ],
"From": [ ]
}
],
"Actions": [
{
"Type": "ReleaseTarget"
},
{
"Type": "ResetInstructions"
},
{
"Type": "PlayAnimation",
"Slot": "Status"
}
]
}
]
}
```
You may have noticed that `"From"` in `"States"` is empty. That means **from any State**. The same applies to `"To"`. You can define more than one **State** in each if you want to. Like going *from* `Idle` *to* your various jobs like `Farm` & `Mine`.
> **Valid configuration**
```json
"States": [
{
"From": [ "Idle" ],
"To": [ ]
}
]
```
> **Also valid configuration**
```json
"States": [
{
"From": [ ],
"To": [ "Idle" ]
}
]
```
## Tricks with states
Using all this knowledge you can do some neat tricks with **States**. You can use **States** as a intermediary evaluator with just the tools provided by Hytale for example. By setting them as a **Sub-state** inside your `Idle` **State** can you cycle through the **Sub-states** in order to do checks when combined with `"InteractionInstructions"` and blocking **Actions**. Hytale does not yet allow you to cancel a blocking **Action** when its running from the outside, so this is where this trick comes in place.
### Case study
You can study my old **Role** I used for my Little Helpers mod for insights on how it was accomplished. A good starting point is to look for the `.AskTask1` **Sub-state** where the cycle starts. This is before I implemented my own Java **Plugin** code to ease this kind of logic.
**Role JSON:** [Template\_Fairy.json](/assets/guides/Template_Fairy.json)
> **From Template\_Fairy.json**
>
> In `Idle` **State**
```json
"Sensor": {
"Type": "State",
"State": ".AskTask1"
}
```
> In `"InteractionInstructions"`
```json
"Sensor": {
"Type":"HasInteracted"
},
"Actions": [
{
"Type":"State",
"State":"Idle.AskTask1"
}
]
```
**Note:** This won't work in Hytale as is because it uses **Plugin** specific **Action & Sensors**. And it uses **Components** that aren't from Hytale either.
## Limitations
You can't check for **Sub-states** inside a `"InteractionInstruction"`, so you have to type out the full **State** name with **Sub-state** included.
While **States** are powerful in their simplicity, they don't allow for sub-**Sub-states**. To achieve that you would want to use the **Flag** system with its own **Action** and **Sensor**.
--- NPC Introduction
Source: https://hytalemodding.dev/en/docs/guides/npc-workings/npc-intro
# NPCs - What are they?
**Definition: NPC** stands for **N**on-**P**layable **C**haracter.
NPCs is what populate the world of Hytale, from the smallest bug to colossal dragons. They all run on the same software while their exterior is only the representation of what they are.
There are also NPCs that can stretch on the definition; they are not really characters but they use the inner-workings of one to perform complicated behavior. An adventurous modder could use it to create intricate static mechanisms if they so feel inclined.
In short, if it can move and reacts to the world it is a NPC. Or **Mob** if you like that term.
## Basics
A NPC has a **Role** from which its functionality comes from. Without one it can't do anything at all. This is where you will spend the grand majority of your time editing. From defining its appearance and max health to laying down intricate **Instructions**.
The NPC goes through many **States** in its lifetime when it interacts and reacts with the world. From idling around to going into a panic or engaging in combat. **States** can be really simple to becoming really intricate branches of **Instructions**.
## What is a role?
You set what *properties* a NPC has in a **Role** and this is where the actual logic rests in. Everything rests inside this JSON file.
The **Role** JSON files must be located in this directory inside your mod: `.\NPC\Roles`
It is vital to place your **Role** JSON files inside ***this*** directory because of how Hytale load mod assets. It is not the same directory structure that Hytale has, so keep that in mind. Other asset types also abide to these strict rules.
## What is a instruction?
**Instructions** are the core of your NPC. They come in many forms but the main one you will be interacting with is the humble `Instructions` block in the JSON file.
It goes from top to bottom when executing the laid down instructions, only ever stopping if the most nested instruction matches its **Sensor**.
```
Scenario:
The NPC's State is Idle
A Player is nearby holding red berries in their hand.
Instructions
Any: We do some check regardless of State (Continue after this)
State: Farming
State: Idle
Player: Has Red Berries
State: Alerted
State: Panicked
```
In this example will it first check the first instruction which as the **Any** sensor. Since it always matches it will execute the **Actions** of it. But since the instruction is told to **Continue** even if it matches it will go to the next one.
The next instruction has the **Sensor** of the type **State** which checks if the NPC is in the state of Farming. But since it isn't it fails to match and go to the next one.
It found a match in the instruction with the **State** of "Idle"! Since it did it will try to execute either **Actions** or **Instructions** nested inside it.
Since there is a nested **Instruction** it will try to execute that. It is a match since there is a player nearby holding red berries in their hand.
You would think that it would simply go out of the `Player: Has Red Berries` instruction now since there is no way but up? Nope, it will keep executing that instruction until it no longer matches. You can make it **Continue** on its way after if you really want to though.
### Addressing the elephant in the room
Can't we have both **Actions** and **Instructions** inside a **Instruction**? No we can't, why you wonder? They are incompatible in how they are executed inside a instruction. Since **Actions** can be blocking which means that the **Instruction** they are in are executed several times until all actions inside it are no longer blocking. This will be gone in-depth in another page.
## What is a sensor?
The sensor is the way NPCs react to the world around them. Without them they will not be able to do anything interesting. A instruction with no sensor will always execute its actions regardless of intent.
```json
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
}
```
> *Mmm, a player has delicious berries within my range...*
## What is a action?
Attacking, equipping, playing animations, make particle effects... The list goes on. Actions are what make NPCs feel alive. They can be of the **blocking** type like `Timeout` which lets you make your NPCs perform actions in a sequence which makes for interesting theatrics.
```json
"ActionsBlocking": true,
"Actions":[
{
"Type":"PlayAnimation",
"Animation":"Happy"
},
{
"Type": "SpawnParticles",
"Offset": [0, 1, 0],
"ParticleSystem": "Hearts",
"TargetNodeName": "Head"
},
{
"Type":"Timeout",
"Delay": [3.0, 3.0]
}
]
```
> *I am so happy my heart can't contain it!*
## But what about Body and Head motion?
They will help your NPC move around in the world. Body motion tells the NPC how to move around with its assigned motion controller. Head motion will tell the NPC how to move its head around.
For moving around the world will you interact with the body motion the most, meanwhile the head motion will be used for looking at things.
The majority of these motion types require a **Target** to be acquired by a **Sensor** in order to be used. But some types like `Wander` for body motion doesn't require one.
```json
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"BodyMotion":{
"Type":"Seek",
"RelativeSpeed": 0.6,
"StopDistance":3,
}
```
> *That person with those berries makes me wanna seek them out for a closer look.*
## So what about states?
I know I avoided talking about **States** until now but its because at its ***core*** are states just another system with its own action and sensor pair. The main differentiator is that Hytale has it tightly integrated with NPCs to the point where its optimized into just being numeric values being checked when matching sensors and setting states.
Are they bad? No, they are in fact the best way to change how a NPC acts in certain situations. Like the standard **Idle** state which tells the NPC how to act when its not in combat or panicking for example. Make it wander around and seem... Alive instead of just being a stationary loot dispenser.
```json
"Instructions":[
{
"Sensor":{
"Type":"State",
"State":"Idle"
},
"Instructions":[
{
"Sensor": {
"Type": "Player",
"Range": 8,
"Filters": [
{
"Type": "ItemInHand",
"Items": ["Plant_Fruit_Berries_Red"]
}
]
},
"HeadMotion": {
"Type": "Watch"
},
"BodyMotion":{
"Type":"Seek",
"RelativeSpeed": 0.6,
"StopDistance":3,
}
},
{
"Reference": "Component_Instruction_Intelligent_Idle_Motion_Wander"
}
]
}
]
```
> *While I'm Idle, if a player with red berries in hand are around I will seek them out. Otherwise I will wander around.*
You may wonder what **"Reference"** is at the last instruction, it will be covered in another page in detail. The very quick version is that it allows you to reuse parts of a JSON in many different files.
## What to do now?
Since you now have an inkling of what is in store for NPCs, the first thing you should do is to extract the **Server** and **Common** folders from the `Assets.zip` file from your Hytale installation. Exclude the `Prefabs` folder from **Server** since we won't need it. The main file we will be referencing a lot is the `Template_Livestock.json` since it contains a lot of useful tricks when it come to instructions.
If you are modding for **Release** it can be found here: `%appdata%\Hytale\install\release\package\game\latest`
If you are modding for **Pre-release** it can be found here: `%appdata%\Hytale\install\pre-release\package\game\latest`
If you installed Hytale in custom directory then you can find Hytale from there instead.
Doing this will make it significantly easier to mod and find other assets like animations, sound effects and particle effects.
Keep in mind that we will be referencing the `Server\NPC\Roles` folder a lot during this guide so keep that folder bookmarked.
## TL;DR
NPCs are derived from **Role** JSON files.
The **Role** JSON files ***must*** be located in this directory inside your mod: `.\NPC\Roles`
NPC roles can be found in the `Server\NPC\Roles` folder in the Hytale `Assets.zip` archive.
**Roles** have **Instructions** with **Sensors, Sub-Instructions, Actions Body motions & Head motions** which tell them how to interact with the world.
**Instructions** is an **Instruction** in itself. That is why you need to put a second **Instructions** inside it.
A **Instruction** with no **Sensor** acts like a **Any** Sensor.
**Actions** are all executed instantly unless **ActionsBlocking** is set to `true` inside the **Instruction**.
--- Example ECS Plugin
Source: https://hytalemodding.dev/en/docs/guides/ecs/example-ecs-plugin
## Practical Example: Poison System
Putting together everything you learned so far, here's a complete poison effect. When applied to any entity, it deals damage at a set interval until it expires and removes itself.
```java
package scot.oskar.hytaletemplate.components;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nullable;
public class PoisonComponent implements Component {
private float damagePerTick;
private float tickInterval;
private int remainingTicks;
private float elapsedTime;
// Add static getter and setter to store componentType in the component instead of getting it from the plugin singleton.
// - This allows anyone to call PoisonComponent.getComponentType() instead of ExamplePlugin.get().getPoisonComponentType();
// - You could also update the setter to be a singleton if desired.
// - Adding a throw condition if getComponentType() returns null could also be good.
private static ComponentType type;
public static ComponentType getComponentType(){
return this.type;
}
public static void setComponentType(ComponentType type){
this.type = type;
}
// A builder codec is needed to allow components to be saved and loaded from disk
public static final BuilderCodec CODEC = BuilderCodec
.builder(PoisonComponent.class, PoisonComponent::new)
.append(
new KeyedCodec<>("DamagePerTick", Codec.FLOAT),
(component, value) -> component.damagePerTick = value,
component -> component.damagePerTick
).add()
.append(
new KeyedCodec<>("TickInterval", Codec.FLOAT),
(component, value) -> component.tickInterval = value,
component -> component.tickInterval
).add()
.append(
new KeyedCodec<>("RemainingTicks", Codec.INTEGER),
(component, value) -> component.remainingTicks = value,
component -> component.remainingTicks
)
.add()
.append(
new KeyedCodec<>("ElapsedTime", Codec.FLOAT),
(component, value) -> component.elapsedTime = value,
component -> component.elapsedTime
)
.add()
.build();
public PoisonComponent() {
this(5f, 1.0f, 10);
}
public PoisonComponent(float damagePerTick, float tickInterval, int totalTicks) {
this.damagePerTick = damagePerTick;
this.tickInterval = tickInterval;
this.remainingTicks = totalTicks;
this.elapsedTime = 0f;
}
public PoisonComponent(PoisonComponent other) {
this.damagePerTick = other.damagePerTick;
this.tickInterval = other.tickInterval;
this.remainingTicks = other.remainingTicks;
this.elapsedTime = other.elapsedTime;
}
@Nullable
@Override
public Component clone() {
return new PoisonComponent(this);
}
public float getDamagePerTick() {
return damagePerTick;
}
public float getTickInterval() {
return tickInterval;
}
public int getRemainingTicks() {
return remainingTicks;
}
public float getElapsedTime() {
return elapsedTime;
}
public void addElapsedTime(float dt) {
this.elapsedTime += dt;
}
public void resetElapsedTime() {
this.elapsedTime = 0f;
}
public void decrementRemainingTicks() {
this.remainingTicks--;
}
public boolean isExpired() {
return this.remainingTicks <= 0;
}
}
```
```java
package scot.oskar.hytaletemplate.systems;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import scot.oskar.hytaletemplate.components.PoisonComponent;
public class PoisonSystem extends EntityTickingSystem {
private final ComponentType poisonComponentType;
public PoisonSystem(ComponentType poisonComponentType) {
this.poisonComponentType = poisonComponentType;
}
@Override
public void tick(float dt, int index, @Nonnull ArchetypeChunk archetypeChunk,
@Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
// You could also just call PoisonComponent.getComponentType() instead of taking in the passed in variable here.
PoisonComponent poison = archetypeChunk.getComponent(index, poisonComponentType);
Ref ref = archetypeChunk.getReferenceTo(index);
poison.addElapsedTime(dt);
if (poison.getElapsedTime() >= poison.getTickInterval()) {
poison.resetElapsedTime();
Damage damage = new Damage(Damage.NULL_SOURCE, DamageCause.OUT_OF_WORLD, poison.getDamagePerTick());
DamageSystems.executeDamage(ref, commandBuffer, damage);
poison.decrementRemainingTicks();
}
if (poison.isExpired()) {
commandBuffer.removeComponent(ref, poisonComponentType);
}
}
@Nullable
@Override
public SystemGroup getGroup() {
return DamageModule.get().getGatherDamageGroup();
}
@Nonnull
@Override
public Query getQuery() {
return Query.and(this.poisonComponentType);
}
}
```
```java
package scot.oskar.hytaletemplate.commands;
public class ExampleCommand extends AbstractPlayerCommand {
public ExampleCommand() {
super("test", "Super test command!");
}
@Override
protected void execute(@Nonnull CommandContext commandContext, @Nonnull Store store,
@Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world) {
Player player = store.getComponent(ref, Player.getComponentType());
PoisonComponent poison = new PoisonComponent(3f, 0.5f, 8);
store.addComponent(ref, PoisonComponent.getComponentType(), poison);
player.sendMessage(Message.raw("You have been poisoned!").color(Color.GREEN).bold(true));
}
}
```
```java
package scot.oskar.hytaletemplate;
public final class ExamplePlugin extends JavaPlugin {
private static ExamplePlugin instance;
public ExamplePlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
}
@Override
protected void setup() {
this.getCommandRegistry().registerCommand(new ExampleCommand());
this.getEventRegistry().registerGlobal(PlayerReadyEvent.class, ExampleEvent::onPlayerReady);
this.getEventRegistry().registerGlobal(PlayerChatEvent.class, ChatFormatter::onPlayerChat);
// Register the component & set the type in the component class so its easier to retrieve.
ComponentType poisonComponentType = this.getEntityStoreRegistry()
.registerComponent(PoisonComponent.class, PoisonComponent::new);
PoisonComponent.setComponentType(poisonComponentType);
this.getEntityStoreRegistry().registerSystem(new PoisonSystem(PoisonComponent.getComponentType()));
}
public static ExamplePlugin get() {
return instance;
}
}
```
The query uses only `poisonComponentType` which means the system will process any entity with a `PoisonComponent`, not just players. This makes it flexible for poisoning NPCs, mobs, or any living entity. The system places itself in the `GatherDamageGroup` so the damage it creates flows through the full damage pipeline including armor reduction and invulnerability checks.
--- Hytale ECS Theory
Source: https://hytalemodding.dev/en/docs/guides/ecs/hytale-ecs-theory
## Store
The `Store` class is the core of Hytale's ECS system, it's responsible for storing entities, if you ever need to access an entity, you need access to the store. It utilises a concept called Archetypes where data is grouped together in chunks. For example if we have 100 Trorks, they will be chunked together along with their components so that they're closely packed together and faster to retrieve.
## EntityStore
When looking through Hytale's server code you will find that most of the time `Store` will be of type `EntityStore`. This name can be misleading as it might suggest that it's a `Store` for entities.
But didn't we just say that the base `Store` already stores entities? The `EntityStore` class implements `WorldProvider` meaning that `EntityStore` is responsible for accessing a specific Hytale `World`. It maintains internal maps `entitiesByUuid` and `networkIdToRef`, allowing you to find a specific entity by its persistent ID or its networking ID.
Every Entity has a `UUIDComponent` as well as a `NetworkId` which are used by the `EntityStore` to lookup entities inside of the `Store`.
## ChunkStore
Another type of `Store` that you might come across is the `ChunkStore`, it is responsible for storing all the components, related to blocks inside of the `World`. You can retrieve `WorldChunk`s which are your general chunk Components.
A `WorldChunk` component contains an `EntityChunk` which holds all the Entities that are inside of the chunk as well as their reference to the EntityStore. It also holds the `BlockChunk` which consists of `BlockSection`s. There are more components making up the overall world and chunk systems but
for now this is the basic understanding for the `ChunkStore`. You can use it to retrieve data about chunks and their blocks as well as entities on a given chunk and create block and chunk systems.
## Holder
A Holder is essentially a blueprint for an entity. Before an entity exists in the Store (and thus in the world), it exists as a `Holder`. It collects and holds all the necessary components (data). You can compare it analogous to shopping cart. You grab all components you need and once you have everything, check out at the store which will take your cart and create a valid entity ID and hand you back a receipt (a Ref).
Let's take a look at an example: initializing players. In `Universe`, the `addPlayer` method demonstrates it perfectly.
When a player connects, we don't immediately throw them into the ECS. We first construct their data in a Holder.
Notice that `PlayerStorage#load` method, which loads player data from disk, returns a `CompletableFuture>`.
What it means is that the method is async and the future will contain a Holder for something in the `EntityStore`.
Just open the `Universe `class, find the `addPlayer` method and read it start to end. Trust me, it will help you a lot when you see the actual process how an entity is constructed, what it has to pass through. In the end, `Universe` calls `world#addPlayer`, which (after dispatching an event) calls the delightful
```java
Ref ref = playerRefComponent.addToStore(store);
```
and `PlayerRef#addToStore` has this:
```java
store.addEntity(this.holder, AddReason.LOAD);
```
## Ref (Reference)
For those familiar with languages like C++, you probably already can guess what this class is purely by the name of it. However, a Ref is a safe "handle" or pointer to an entity. You should **NEVER** store a direct reference to an entity object, you use a Ref instead. It tracks whether an entity is still alive. If you call `validate()` on a Ref for an entity that has been deleted, it throws an exception.
## Player Components
In Hytale, a "Player" is not just one object. It is a single entity composed of multiple specialized components. Understanding the difference between `Player` and `PlayerRef` is crucial for modding.
### PlayerRef
Despite its name, PlayerRef is a Component, not a handle. It represents the player's connection and identity. It's a special component which stays active as long as the player is connected to the server, even if the player switches worlds. The key data that it stores are the player's username, UUID, language as well as the packet handler.
### Player
The `Player` component represents the player's physical presence. It only exists when the player is actually spawned in a world. Providing access to gameplay specific data, this component differs per world.
To interact with an entity, you use the `Store` to retrieve its components via their `ComponentType`. Because Hytale uses a decoupled system, you don't call `entity.getHealth()`. Instead, you ask the `Store` for the health data associated with that entity's `Ref`.
```java
@Override
protected void execute(@Nonnull CommandContext commandContext, @Nonnull Store store,
@Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world) {
Player player = store.getComponent(ref, Player.getComponentType());
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
player.sendMessage(Message.raw("UUIDComponent : " + component.getUuid()));
player.sendMessage(Message.raw("Transform : " + transform.getPosition()));
}
```
In here we use the `Store` to access the `Player` component using the `Ref`. We can do the same for other components like the `UUIDComponent` or the `TransformComponent` to retrieve the entity Transform containing the position and rotation.
## Components
Components are pure data containers. They hold state but contain no logic. In Hytale, components must implement `Component` and provide a clone method for the ECS to copy them when needed.
```java
public class PoisonComponent implements Component {
private float damagePerTick;
private float tickInterval;
private int remainingTicks;
private float elapsedTime;
public PoisonComponent() {
this(5f, 1.0f, 10);
}
public PoisonComponent(float damagePerTick, float tickInterval, int totalTicks) {
this.damagePerTick = damagePerTick;
this.tickInterval = tickInterval;
this.remainingTicks = totalTicks;
this.elapsedTime = 0f;
}
public PoisonComponent(PoisonComponent other) {
this.damagePerTick = other.damagePerTick;
this.tickInterval = other.tickInterval;
this.remainingTicks = other.remainingTicks;
this.elapsedTime = other.elapsedTime;
}
@Nullable
@Override
public Component clone() {
return new PoisonComponent(this);
}
public float getDamagePerTick() {
return damagePerTick;
}
public float getTickInterval() {
return tickInterval;
}
public int getRemainingTicks() {
return remainingTicks;
}
public float getElapsedTime() {
return elapsedTime;
}
public void addElapsedTime(float dt) {
this.elapsedTime += dt;
}
public void resetElapsedTime() {
this.elapsedTime = 0f;
}
public void decrementRemainingTicks() {
this.remainingTicks--;
}
public boolean isExpired() {
return this.remainingTicks <= 0;
}
}
```
The default constructor is required for the registration factory. The copy constructor is used by `clone()` which the ECS calls internally when it needs to duplicate component data.
## CommandBuffer
The `CommandBuffer` queues changes to entities. Use it instead of modifying the store directly to ensure thread safety and proper ordering. You'll use it to add components, remove components, and execute damage.
```java
commandBuffer.addComponent(ref, componentType, new MyComponent());
commandBuffer.removeComponent(ref, componentType);
MyComponent comp = commandBuffer.getComponent(ref, componentType);
```
## Codec
Codecs handle serialization and deserialization of components. Hytale uses them to save and load entity data to and from disk as well as sending component data over the network. When creating a custom component, you must also create a corresponding Codec.
There are multiple Codec types already implemented in the default Codec Interface:
* Codec.STRING
* Codec.BOOLEAN
* Codec.DOUBLE
* Codec.FLOAT
* Codec.BYTE
* Codec.SHORT
* Codec.INTEGER
* Codec.LONG
* Codec.DOUBLE\_ARRAY
* Codec.FLOAT\_ARRAY
* Codec.INT\_ARRAY
* Codec.LONG\_ARRAY
* Codec.STRING\_ARRAY
* Codec.PATH
* Codec.INSTANT
* Codec.DURATION
* Codec.DURATION\_SECONDS
* Codec.LOG\_LEVEL
* Codec.UUID\_BINARY
* Codec.UUID\_STRING
Aside from the basic types, there are also more complex Codec implementations like `CodecMap`, `ObjectMapCodec` or `EnumCodec`.
### Builder Codec
The `BuilderCodec` is a powerful utility for creating your custom codecs. It allows you to define how each field in your component is serialized and deserialized. Each Field needs to have the following information:
* The KeyedCodec to use for serialization/deserialization of the field. This can be one of the built-in codecs or a custom codec if your field is a complex type. To initialize a KeyedCodec you need to provide a unique string identifier for the codec and the actual Codec instance to use for the field.
Keep in mind that every KeyedCodec identifier string must start Uppercase and be unique across your entire mod. This means that if you have multiple components, each field's KeyedCodec identifier must not clash with any other field's identifier in any other component within your mod. Otherwise you may run into serialization issues.
* A setter function to set the field value on the component
* A getter function to retrieve the field value from the component
This might seem tedious at first but it ensures that your component can be correctly serialized and deserialized by the ECS system. Let's take a look at each of the required parameters in detail:
* **KeyedCodec**: This defines how the data is converted to and from a storable format. For example, if you have an integer field, you would use `Codec.INTEGER`.
```java
// Example of creating a KeyedCodec for a String field
KeyedCodec example = new KeyedCodec("ExampleIdForCodec", Codec.STRING);
// Example of creating a KeyedCodec for an Integer field
KeyedCodec exampleInt = new KeyedCodec("ExampleIntIdForCodec", Codec.INTEGER);
```
* **Setter Function**: This is a lambda function that takes an instance of your component + the value to set and sets the field value on the component. For example, if you have a field called `myCustomField` in your custom component, your setter function would look like this:
```java
(data, value) -> data.myCustomField = value
```
* **Getter Function**: This is a lambda function that takes an instance of your component and returns the value of the field you want to serialize. For example, if you have a field called `myCustomField` in your custom component, your getter function would look like this:
```java
(data) -> data.myCustomField
```
Lambda functions are a concise way to represent functional interfaces in Java. They allow you to pass behavior as parameters, making your code more flexible and reusable. In the context of Codec creation, they enable you to define how to get and set field values without needing to create separate classes or methods for each field.
If you are unfamiliar with lambda functions, you can view them as short and compressed methods that can be defined inline. They are particularly useful for scenarios where you need to pass simple behavior, such as getting or setting a value, without the overhead of creating a full class or method.
As an example, consider the following lambda function used as a getter:
```java
(data) -> data.myCustomField
```
This lambda takes a single parameter `data` (which would be an instance of your component) and returns the value of `myCustomField`. It's equivalent to writing a method like this:
```java
public Object getMyCustomField(MyComponent data) {
return data.myCustomField;
}
```
After defining the necessary parameters, you can create a `BuilderCodec` for your component. As example let's look at how to create a Codec for the previously defined `PoisonComponent`. For the purpose of this example, let's assume that the `PoisonComponent` has only the following fields:
* `damagePerTick` (float)
* `poisonName` (String)
```java
public class PoisonComponent implements Component {
private float damagePerTick;
private String poisonName;
// Constructors, getters, setters, clone method omitted for brevity
public static final BuilderCodec CODEC = BuilderCodec.builder(PoisonComponent.class, PoisonComponent::new)
.append(
new KeyedCodec("DamagePerTick", Codec.FLOAT),
(data, value) -> data.damagePerTick = value,
(data) -> data.damagePerTick
)
.add()
.append(
new KeyedCodec("PoisonName", Codec.STRING),
(data, value) -> data.poisonName = value,
(data) -> data.poisonName
)
.add()
.build();
}
```
You can also add validators to your Codec fields to ensure that the data being serialized/deserialized meets certain criteria. For example, if you want to ensure that the `damagePerTick` field is always non-negative or the poison name is never null, you can add a validator like this:
```java
.append(
new KeyedCodec("DamagePerTick", Codec.FLOAT),
(data, value) -> data.damagePerTick = value,
(data) -> data.damagePerTick
)
.addValidator(Validators.greaterThan(0))
.add()
.append(
new KeyedCodec("PoisonName", Codec.STRING),
(data, value) -> data.poisonName = value,
(data) -> data.poisonName
)
.addValidator(Validators.nonNull())
.add()
```
### Complex Codec Examples
If you want to have a map inside your component, you can use the MapCodec class to build a KeyedCodec for it. Here's an example of how to create a Codec for a Map field:
```java
var damagePerTick = new KeyedCodec<>("DamagePerTick", new MapCodec<>(Codec.FLOAT, HashMap::new));
```
This also works with custom Objects as long as you have a Codec defined for the Object type.
--- Entity Component System
Source: https://hytalemodding.dev/en/docs/guides/ecs/entity-component-system
This guide requires basic understanding of OOP concepts such as inheritance.
Please make sure you have a good understanding of these concepts before reading this guide.
This guide is currently only on the theory of ECS and will not have code examples.
Code examples are intentionally omitted to avoid confusion about how Hytale’s ECS may be implemented.
# Introduction
An Entity Component System otherwise known as ECS is a design pattern in game development.
ECS is made up of Entities, which reference Components and Systems that operates on Entities.
It also focuses on composition over inheritance, eliminating rigid hierarchy, helps separate behavior and data and makes reusability easier.
# Composition Over Inheritance
Before getting into understanding what ECS is made of, lets understand what is the ECS philosophy.
We can describe composition as "has X" and inheritance as "is X".
When we inherit a class in Java, we inherit all of its attributes and methods.
While in composition we can choose for each object what it references and what systems will work on it.
# Entities, Components and Systems
## Entities
An Entity is just a unique identifier.
* It contains no data.
* It contains no logic
Think of entities as nouns that can be described using components
## Components
Components are just plain data containers.
* They store only data
* They contain no behavior or logic
* They describe traits or aspects of an entity
Examples of components:
* A 'Position' component that stores X, Y and Z coordinates
* A 'Velocity' component that stores the speed along each axis
* A 'Health' component that stores the current health of the entity
Think of components as adjectives that describe an entity
## Systems
Systems contains the logic for your game (or in this case, a mod).
* It Operates on all entities that have a specific set of components
* It Does not store entity data itself
* It Applies behavior by reading and modifying component data
For example:
* A "WinConditionSystem" may process every Entity that contains the components "Position" and "Velocity"
* If an entity reaches the position `(0, 0, 0)`, the system will trigger a "You Won!" message.
--- Block Components
Source: https://hytalemodding.dev/en/docs/guides/ecs/block-components
# Overview
In this guide, you'll learn how to use block components to create a ticking block.
For a more complete reference, you may also want to review FarmingSystems.Ticking, as much of the underlying logic is based on its implementation.
## Steps
### 1. ExampleBlock - holds block behavior
```java
public class ExampleBlock implements Component {
public static final BuilderCodec CODEC = BuilderCodec.builder(ExampleBlock.class, ExampleBlock::new).build();
// Components usually require a copy constructor as well but since we don't actually hold any data in this component this is not neccessary
public ExampleBlock() { }
public static ComponentType getComponentType() {
return ExamplePlugin.get().getExampleBlockComponentType();
}
@Nullable
public Component clone() {
return new ExampleBlock();
}
}
```
ExampleBlock is a custom component that stores the behavior and data for your ticking block. Here, it simply places an Ice Block at x + 1 relative to its current position when it ticks.
***
### 2. ExampleInitializer - marks blocks as ticking when placed
```java
public class ExampleInitializer extends RefSystem {
@Override
public void onEntityAdded(@Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
BlockModule.BlockStateInfo info = (BlockModule.BlockStateInfo) commandBuffer.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
if (info == null) return;
ExampleBlock generator = (ExampleBlock) commandBuffer.getComponent(ref, ExamplePlugin.get().getExampleBlockComponentType());
if (generator != null) {
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
WorldChunk worldChunk = (WorldChunk) commandBuffer.getComponent(info.getChunkRef(), WorldChunk.getComponentType());
if (worldChunk != null) {
worldChunk.setTicking(x, y, z, true);
}
}
}
@Override
public void onEntityRemove(@Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
}
@Override
public Query getQuery() {
return Query.and(BlockModule.BlockStateInfo.getComponentType(), ExamplePlugin.get().getExampleBlockComponentType());
}
}
```
ExampleInitializer is a RefSystem that reacts when block entities with the ExampleBlock component are added or removed. This is crucial for marking blocks as ticking when they're first placed.
**Key Points:**
* Tells the game that this block should tick, allowing `ExampleSystem` to process it.
```java
worldChunk.setTicking(x, y, z, true);
```
***
### 3. ExampleSystem - handles ticking
```java
public class ExampleSystem extends EntityTickingSystem {
public void tick(float dt, int index, @Nonnull ArchetypeChunk archetypeChunk, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
BlockSection blocks = (BlockSection) archetypeChunk.getComponent(index, BlockSection.getComponentType());
assert blocks != null;
if (blocks.getTickingBlocksCountCopy() != 0) {
ChunkSection section = (ChunkSection) archetypeChunk.getComponent(index, ChunkSection.getComponentType());
assert section != null;
BlockComponentChunk blockComponentChunk = (BlockComponentChunk) commandBuffer.getComponent(section.getChunkColumnReference(), BlockComponentChunk.getComponentType());
assert blockComponentChunk != null;
blocks.forEachTicking(blockComponentChunk, commandBuffer, section.getY(), (blockComponentChunk1, commandBuffer1, localX, localY, localZ, blockId) -> {
Ref blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(localX, localY, localZ));
if (blockRef == null) {
return BlockTickStrategy.IGNORED;
} else {
ExampleBlock exampleBlock = (ExampleBlock) commandBuffer1.getComponent(blockRef, ExampleBlock.getComponentType());
if (exampleBlock != null) {
WorldChunk worldChunk = (WorldChunk) commandBuffer.getComponent(section.getChunkColumnReference(), WorldChunk.getComponentType());
World world = worldChunk.getWorld();
int globalX = localX + (worldChunk.getX() * 32);
int globalZ = localZ + (worldChunk.getZ() * 32);
// We need to execute setBlock on the world thread as you cannot call store functions from a system
// This is because of the architecture of the server, depending on your needs you can also use the CommandBuffer
world.execute(() -> {
world.setBlock(globalX + 1, localY, globalZ, "Rock_Ice");
});
return BlockTickStrategy.CONTINUE;
} else {
return BlockTickStrategy.IGNORED;
}
}
});
}
}
@Nullable
public Query getQuery() {
return Query.and(BlockSection.getComponentType(), ChunkSection.getComponentType());;
}
}
```
ExampleSystem is an EntityTickingSystem that runs every tick to execute the logic for all ticking blocks with the ExampleBlock component.
**Key Points:**
* **Get the block component**
```java
ExampleBlock exampleBlock = (ExampleBlock) commandBuffer.getComponent(blockRef, ExampleBlock.getComponentType());
```
* **Convert local chunk coordinates to world coordinates**
```java
int globalX = localX + (worldChunk.getX() * 32);
int globalZ = localZ + (worldChunk.getZ() * 32);
```
* **Run the block logic**
```java
exampleBlock.runBlockAction(globalX, localY, globalZ, worldChunk.getWorld());
```
* **Keep the block ticking in the next tick**
```java
return BlockTickStrategy.CONTINUE;
```
***
### 4. ExamplePlugin - registers components and systems
```java
public class ExamplePlugin extends JavaPlugin {
protected static ExamplePlugin instance;
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private ComponentType exampleBlockComponentType;
public static ExamplePlugin get() {
return instance;
}
public ExamplePlugin(@Nonnull JavaPluginInit init) {
super(init);
LOGGER.atInfo().log("Hello from " + this.getName() + " version " + this.getManifest().getVersion().toString());
}
@Override
protected void setup() {
instance = this;
LOGGER.atInfo().log("Setting up plugin " + this.getName());
this.exampleBlockComponentType = this.getChunkStoreRegistry().registerComponent(ExampleBlock.class, "ExampleBlock", ExampleBlock.CODEC);
}
@Override
protected void start() {
this.getChunkStoreRegistry().registerSystem(new ExampleSystem());
this.getChunkStoreRegistry().registerSystem(new ExampleInitializer());
}
public ComponentType getExampleBlockComponentType() {
return this.exampleBlockComponentType;
}
}
```
***
### 5. Configure In-Game
With this logic, the block will now continuously place an Ice Block at the coordinates x + 1 relative to its current position every time it ticks.
***
## Common Issues
### NullPointerException on Startup
**Error message:**
```java
java.lang.NullPointerException: Cannot invoke "com.hypixel.hytale.component.query.Query.validateRegistry(com.hypixel.hytale.component.ComponentRegistry)" because "query" is null
```
**Cause:**
This error occurs when your module is loaded **before** the required Hytale modules.
**Fix:**
Add `EntityModule` and `BlockModule` as dependencies in your `manifest.json` to ensure proper load order.
```json
"Dependencies": {
"Hytale:EntityModule": "*",
"Hytale:BlockModule": "*"
}
```
--- Systems
Source: https://hytalemodding.dev/en/docs/guides/ecs/systems
Systems are where the logic lives. While components are pure data containers, systems operate on entities that match specific component queries. The ECS scheduler runs systems each tick, passing in only the entities that have the components the system cares about.
### EntityTickingSystem
The most common system type. It runs every tick and processes each entity matching its query individually.
```java
public class PoisonSystem extends EntityTickingSystem {
private final ComponentType poisonComponentType;
public PoisonSystem(ComponentType poisonComponentType) {
this.poisonComponentType = poisonComponentType;
}
@Override
public void tick(float dt, int index, @Nonnull ArchetypeChunk archetypeChunk,
@Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
PoisonComponent poison = archetypeChunk.getComponent(index, poisonComponentType);
Ref ref = archetypeChunk.getReferenceTo(index);
poison.addElapsedTime(dt);
if (poison.getElapsedTime() >= poison.getTickInterval()) {
poison.resetElapsedTime();
Damage damage = new Damage(Damage.NULL_SOURCE, DamageCause.OUT_OF_WORLD, poison.getDamagePerTick());
DamageSystems.executeDamage(ref, commandBuffer, damage);
poison.decrementRemainingTicks();
}
if (poison.isExpired()) {
commandBuffer.removeComponent(ref, poisonComponentType);
}
}
@Nullable
@Override
public SystemGroup getGroup() {
return DamageModule.get().getGatherDamageGroup();
}
@Nonnull
@Override
public Query getQuery() {
return Query.and(this.poisonComponentType);
}
}
```
The `tick` method receives `dt` which is delta time since the last tick. This lets you accumulate time for interval-based logic rather than counting ticks. The `ArchetypeChunk` gives you access to the entity's components via the index, and `getReferenceTo` returns the `Ref` you need for issuing commands.
### TickingSystem
Runs once per tick globally, not per-entity. Use this for world-wide updates or logic that doesn't target specific entities.
```java
public class GlobalUpdateSystem extends TickingSystem {
@Override
public void tick(float dt, int index, Store store) {
World world = store.getExternalData().getWorld();
}
}
```
### DelayedEntitySystem
Like `EntityTickingSystem` but with a built-in delay. The constructor takes a float representing seconds between executions.
```java
public class HealthRegenSystem extends DelayedEntitySystem {
public HealthRegenSystem() {
super(1.0f);
}
@Override
public void tick(float dt, int index, @Nonnull ArchetypeChunk archetypeChunk,
@Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
// Runs every 1 second per matching entity
}
@Nonnull
@Override
public Query getQuery() {
return Query.and(Player.getComponentType());
}
}
```
### RefSystem
We can also create systems to listen for changes on entities themselves. This is done through a `RefSystem`, for example let's say we want to perform an action whenever we add a component to an entity, update it or remove it.
This can be done via the `RefChangeSystem`. Let's break down the example below:
```java
public class PermissionAttachmentSystem extends RefChangeSystem {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
@Nonnull
@Override
public ComponentType componentType() {
return EntityStoreRegistry.get().getPermissionAttachmentComponentType();
}
@Override
public void onComponentAdded(@Nonnull Ref ref, @Nonnull PermissionAttachment permissionAttachment, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
UUID playerUuid = component.getUuid();
// PermissionAttachment component was added to a new entity
}
@Override
public void onComponentSet(@Nonnull Ref ref, @Nullable PermissionAttachment oldAttachment, @Nonnull PermissionAttachment newAttachment, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
UUID playerUuid = component.getUuid();
// PermissionAttachment component was updated using replaceComponent or putComponent
}
@Override
public void onComponentRemoved(@Nonnull Ref ref, @Nonnull PermissionAttachment permissionAttachment, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
UUID playerUuid = component.getUuid();
// PermissionAttachment component was removed from an entity
}
@Nullable
@Override
public Query getQuery() {
return EntityStoreRegistry.get().getPermissionAttachmentComponentType();
}
}
```
In this system we listen for changes on the `PermissionAttachment` component inside of the `EntityStore`. This gives us access to `onComponentAdded`, `onComponentSet` and `onComponentRemoved` which in this example caches and persists permission data
which is stored on a player inside of a custom `PermissionAttachment` component.
## Queries
Queries filter which entities a system processes. The ECS only passes entities to your system's `tick` method if they have all the components specified in the query.
```java
// Single component - any entity with PoisonComponent
Query.and(poisonComponentType)
// Multiple components - entities with both
Query.and(poisonComponentType, Player.getComponentType())
// Exclusion - players that aren't dead
Query.and(Player.getComponentType(), Query.not(DeathComponent.getComponentType()))
```
## SystemGroups and Dependencies
Systems can declare which group they belong to and what dependencies they have. This controls execution order which is critical for systems that interact.
```java
@Nullable
@Override
public SystemGroup getGroup() {
return DamageModule.get().getGatherDamageGroup();
}
```
For more complex ordering you can override `getDependencies`:
```java
@Nonnull
public Set> getDependencies() {
return Set.of(
new SystemGroupDependency(Order.AFTER, DamageModule.get().getFilterDamageGroup()),
new SystemDependency(Order.BEFORE, PlayerSystems.ProcessPlayerInput.class)
);
}
```
The damage system is a good example of why ordering matters. Hytale's damage pipeline has four stages: GatherDamageGroup collects damage sources, FilterDamageGroup applies reductions and cancellations, then damage is applied to health, and finally InspectDamageGroup handles side effects like particles and sounds. If these ran in the wrong order you'd play death animations before the entity dies or apply armor reduction after health is already subtracted.
## Registering Components and Systems
Components and systems must be registered during plugin setup. The `EntityStoreRegistry` handles this.
```java
public final class ExamplePlugin extends JavaPlugin {
private static ExamplePlugin instance;
private ComponentType poisonComponent;
public ExamplePlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
}
@Override
protected void setup() {
this.getCommandRegistry().registerCommand(new ExampleCommand());
this.getEventRegistry().registerGlobal(PlayerReadyEvent.class, ExampleEvent::onPlayerReady);
this.getEventRegistry().registerGlobal(PlayerChatEvent.class, ChatFormatter::onPlayerChat);
this.poisonComponent = this.getEntityStoreRegistry()
.registerComponent(PoisonComponent.class, PoisonComponent::new);
this.getEntityStoreRegistry().registerSystem(new PoisonSystem(this.poisonComponent));
}
public ComponentType getPoisonComponentType() {
return poisonComponent;
}
public static ExamplePlugin get() {
return instance;
}
}
```
The `registerComponent` method returns a `ComponentType` which acts as a key for accessing that component type throughout your plugin. Store it as a field and pass it to any systems that need it. The second argument is a factory for creating default instances.
--- 11 - HashMap and Key-Value Storage
Source: https://hytalemodding.dev/en/docs/guides/java-basics/11-hashmaps
A `HashMap` stores data in key-value pairs, like a real-world dictionary. You look up a word (key) to find its definition (value). This is perfect for player data, item properties, and configuration settings.
## HashMap Basics
```java
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
// Create a HashMap (String keys, Integer values)
HashMap playerLevels = new HashMap<>();
// Add key-value pairs
playerLevels.put("Alice", 10);
playerLevels.put("Bob", 15);
playerLevels.put("Charlie", 8);
// Get a value by key
int aliceLevel = playerLevels.get("Alice"); // 10
System.out.println("Alice is level " + aliceLevel);
}
}
```
| **HashMap** | **ArrayList** |
| ------------------------------- | ----------------------------- |
| Unordered | Ordered by index (0, 1, 2...) |
| Access by key (can be any type) | Access by position |
| Good for looking up values | Good for lists of items |
| Each key is unique | Can have duplicates |
```java
// ArrayList - access by number
ArrayList players = new ArrayList<>();
players.add("Alice");
String player = players.get(0); // Get first player
// HashMap - access by name
HashMap levels = new HashMap<>();
levels.put("Alice", 10);
int level = levels.get("Alice"); // Get Alice's level
```
## Common HashMap Methods
### Adding and Updating
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100); // Add new entry
scores.put("Bob", 150);
scores.put("Alice", 200); // Update existing (Alice now 200)
```
### Getting Values
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100);
int score = scores.get("Alice"); // 100
Integer missing = scores.get("Dave"); // null (doesn't exist)
// Get with default value
int score2 = scores.getOrDefault("Dave", 0); // 0 (returns default)
```
### Checking for Keys/Values
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100);
boolean hasAlice = scores.containsKey("Alice"); // true
boolean hasDave = scores.containsKey("Dave"); // false
boolean has100 = scores.containsValue(100); // true
boolean has200 = scores.containsValue(200); // false
```
### Removing Entries
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100);
scores.put("Bob", 150);
scores.remove("Alice"); // Remove by key
System.out.println(scores); // {Bob=150}
scores.clear(); // Remove everything
System.out.println(scores); // {}
```
### Size and Empty Check
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100);
int size = scores.size(); // 1
boolean empty = scores.isEmpty(); // false
```
## Looping Through HashMap
### Loop Through Keys
```java
HashMap scores = new HashMap<>();
scores.put("Alice", 100);
scores.put("Bob", 150);
scores.put("Charlie", 75);
for (String name : scores.keySet()) {
System.out.println(name);
}
// Output: Alice, Bob, Charlie (order may vary)
```
### Loop Through Values
```java
for (Integer score : scores.values()) {
System.out.println(score);
}
// Output: 100, 150, 75 (order may vary)
```
### Loop Through Both Keys and Values
```java
for (String name : scores.keySet()) {
int score = scores.get(name);
System.out.println(name + ": " + score);
}
// Or using entrySet (more efficient)
for (var entry : scores.entrySet()) {
String name = entry.getKey();
int score = entry.getValue();
System.out.println(name + ": " + score);
}
```
HashMap does **not** maintain insertion order! If you need order, use `LinkedHashMap`:
```java
import java.util.LinkedHashMap;
LinkedHashMap orderedScores = new LinkedHashMap<>();
orderedScores.put("Alice", 100);
orderedScores.put("Bob", 150);
orderedScores.put("Charlie", 75);
// Will print in insertion order
for (String name : orderedScores.keySet()) {
System.out.println(name);
}
// Output: Alice, Bob, Charlie (guaranteed order)
```
## Practical Examples
### Player Statistics System
```java
import java.util.HashMap;
public class PlayerStats {
private HashMap stats;
public PlayerStats() {
this.stats = new HashMap<>();
// Initialize default stats
stats.put("health", 100);
stats.put("mana", 50);
stats.put("strength", 10);
stats.put("defense", 5);
stats.put("speed", 8);
}
public int getStat(String statName) {
return stats.getOrDefault(statName, 0);
}
public void setStat(String statName, int value) {
stats.put(statName, value);
}
public void modifyStat(String statName, int amount) {
int current = getStat(statName);
stats.put(statName, current + amount);
}
public void displayStats() {
System.out.println("=== Player Stats ===");
for (var entry : stats.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
PlayerStats stats = new PlayerStats();
stats.displayStats();
stats.modifyStat("strength", 5);
stats.modifyStat("health", -20);
System.out.println("\nAfter modifications:");
stats.displayStats();
}
}
```
### Item Properties Database
```java
import java.util.HashMap;
public class ItemDatabase {
private HashMap> items;
public ItemDatabase() {
this.items = new HashMap<>();
}
public void addItem(String itemName) {
HashMap properties = new HashMap<>();
properties.put("damage", 0);
properties.put("durability", 100);
properties.put("rarity", "Common");
properties.put("stackable", true);
items.put(itemName, properties);
}
public void setProperty(String itemName, String property, Object value) {
if (items.containsKey(itemName)) {
items.get(itemName).put(property, value);
}
}
public Object getProperty(String itemName, String property) {
if (items.containsKey(itemName)) {
return items.get(itemName).get(property);
}
return null;
}
public void displayItem(String itemName) {
if (!items.containsKey(itemName)) {
System.out.println("Item not found!");
return;
}
System.out.println("=== " + itemName + " ===");
HashMap props = items.get(itemName);
for (var entry : props.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
ItemDatabase db = new ItemDatabase();
db.addItem("Iron Sword");
db.setProperty("Iron Sword", "damage", 15);
db.setProperty("Iron Sword", "rarity", "Uncommon");
db.displayItem("Iron Sword");
}
}
```
### Configuration Manager
```java
import java.util.HashMap;
public class GameConfig {
private HashMap settings;
public GameConfig() {
this.settings = new HashMap<>();
loadDefaults();
}
private void loadDefaults() {
settings.put("difficulty", "normal");
settings.put("musicVolume", "50");
settings.put("sfxVolume", "50");
settings.put("renderDistance", "10");
settings.put("showFPS", "false");
}
public String getSetting(String key) {
return settings.getOrDefault(key, "");
}
public void setSetting(String key, String value) {
settings.put(key, value);
System.out.println("Set " + key + " to " + value);
}
public int getIntSetting(String key) {
String value = getSetting(key);
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return 0;
}
}
public boolean getBooleanSetting(String key) {
return "true".equalsIgnoreCase(getSetting(key));
}
public void displaySettings() {
System.out.println("=== Game Settings ===");
for (var entry : settings.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
```
### Player Inventory with Quantities
```java
import java.util.HashMap;
public class Inventory {
private HashMap items;
private int maxSlots;
public Inventory(int maxSlots) {
this.items = new HashMap<>();
this.maxSlots = maxSlots;
}
public boolean addItem(String itemName, int quantity) {
if (items.size() >= maxSlots && !items.containsKey(itemName)) {
System.out.println("Inventory full!");
return false;
}
int current = items.getOrDefault(itemName, 0);
items.put(itemName, current + quantity);
System.out.println("Added " + quantity + "x " + itemName);
return true;
}
public boolean removeItem(String itemName, int quantity) {
if (!items.containsKey(itemName)) {
System.out.println("Don't have that item!");
return false;
}
int current = items.get(itemName);
if (current < quantity) {
System.out.println("Not enough " + itemName);
return false;
}
if (current == quantity) {
items.remove(itemName);
} else {
items.put(itemName, current - quantity);
}
System.out.println("Removed " + quantity + "x " + itemName);
return true;
}
public int getQuantity(String itemName) {
return items.getOrDefault(itemName, 0);
}
public boolean hasItem(String itemName) {
return items.containsKey(itemName);
}
public void displayInventory() {
System.out.println("\n=== Inventory (" + items.size() + "/" + maxSlots + " slots) ===");
if (items.isEmpty()) {
System.out.println("Empty");
} else {
for (var entry : items.entrySet()) {
System.out.println(entry.getKey() + " x" + entry.getValue());
}
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
Inventory inv = new Inventory(10);
inv.addItem("Wood", 64);
inv.addItem("Stone", 32);
inv.addItem("Wood", 20); // Adds to existing
inv.displayInventory();
inv.removeItem("Wood", 50);
inv.displayInventory();
}
}
```
### Cooldown Manager
```java
import java.util.HashMap;
public class CooldownManager {
private HashMap cooldowns;
public CooldownManager() {
this.cooldowns = new HashMap<>();
}
public void startCooldown(String ability, long durationMs) {
long endTime = System.currentTimeMillis() + durationMs;
cooldowns.put(ability, endTime);
System.out.println(ability + " on cooldown for " + (durationMs / 1000) + " seconds");
}
public boolean isOnCooldown(String ability) {
if (!cooldowns.containsKey(ability)) {
return false;
}
long endTime = cooldowns.get(ability);
long now = System.currentTimeMillis();
if (now >= endTime) {
cooldowns.remove(ability);
return false;
}
return true;
}
public long getRemainingTime(String ability) {
if (!isOnCooldown(ability)) {
return 0;
}
long endTime = cooldowns.get(ability);
long now = System.currentTimeMillis();
return (endTime - now) / 1000; // Return seconds
}
public boolean useAbility(String ability, long cooldownSeconds) {
if (isOnCooldown(ability)) {
long remaining = getRemainingTime(ability);
System.out.println(ability + " is on cooldown (" + remaining + "s remaining)");
return false;
}
System.out.println("Used " + ability + "!");
startCooldown(ability, cooldownSeconds * 1000);
return true;
}
}
```
## HashMap with Custom Objects
Store your own classes as values:
```java
public class Player {
private String name;
private int level;
private int health;
public Player(String name, int level) {
this.name = name;
this.level = level;
this.health = 100;
}
// Getters and setters...
@Override
public String toString() {
return name + " (Lv. " + level + ", HP: " + health + ")";
}
}
public class Main {
public static void main(String[] args) {
HashMap players = new HashMap<>();
players.put("player1", new Player("Alice", 10));
players.put("player2", new Player("Bob", 15));
// Get and use player
Player alice = players.get("player1");
System.out.println(alice);
}
}
```
## Nested HashMaps
HashMaps can contain other HashMaps:
```java
// Store multiple properties per player
HashMap> playerData = new HashMap<>();
// Add player with stats
HashMap aliceStats = new HashMap<>();
aliceStats.put("level", 10);
aliceStats.put("health", 100);
aliceStats.put("mana", 50);
playerData.put("Alice", aliceStats);
// Access nested data
int aliceLevel = playerData.get("Alice").get("level");
System.out.println("Alice's level: " + aliceLevel);
```
## Practice Exercises
1. **Phone Book**: Create a phone book that stores names and phone numbers. Add methods to:
* Add contact
* Find number by name
* Delete contact
* Display all contacts
2. **Grade Manager**: Store student names and grades. Create methods to:
* Add student with grade
* Update grade
* Calculate class average
* Find highest grade
3. **Word Counter**: Write a program that counts how many times each word appears in a sentence using a HashMap.
4. **Item Shop**: Create a shop where items have names and prices. Add methods to:
* Add item with price
* Get item price
* Apply discount to all items
* Display all items and prices
***
--- 03 - Control Flow If Statements
Source: https://hytalemodding.dev/en/docs/guides/java-basics/03-control-flow-if
Programs need to make decisions. If statements let your code choose different paths based on conditions.
## Basic If Statement
The simplest form checks one condition:
```java
int health = 20;
if (health > 10) {
System.out.println("You are healthy!");
}
```
### Structure
```java
if (condition) {
// code to execute if condition is true
}
```
## If-Else Statements
Choose between two paths:
```java
int health = 5;
if (health > 10) {
System.out.println("You are healthy!");
} else {
System.out.println("You need healing!");
}
```
## If-Else Else-If Chain
Check multiple conditions in sequence:
```java
int health = 7;
if (health > 15) {
System.out.println("You are in great shape!");
} else if (health > 5) {
System.out.println("You are okay.");
} else {
System.out.println("You need healing!");
}
```
## Nested If Statements
You can place if statements inside other if statements:
```java
int health = 12;
if (health > 10) {
if (health > 15) {
System.out.println("You are in great shape!");
} else {
System.out.println("You are healthy!");
}
} else {
System.out.println("You need healing!");
}
```
## Compound Conditions
You can combine multiple conditions using logical operators:
```java
// AND - both must be true
int health = 12;
boolean hasPotion = true;
if (health > 10 && hasPotion) {
System.out.println("You are healthy and have a potion!");
} else if (health > 10) {
System.out.println("You are healthy but need a potion!");
} else {
System.out.println("You need healing!");
}
// OR - at least one must be true
int health = 8;
boolean hasPotion = false;
if (health > 10 || hasPotion) {
System.out.println("You are either healthy or have a potion!");
} else {
System.out.println("You need healing and a potion!");
}
// NOT - reverses the condition
int health = 8;
if (!(health > 10)) {
System.out.println("You need healing!");
} else {
System.out.println("You are healthy!");
}
```
## Ternary Operator
A compact way to assign values:
```java
int health = 12;
String status = (health > 10) ? "Healthy" : "Needs Healing";
```
### Syntax
```java
variable = (condition) ? valueIfTrue : valueIfFalse;
```
--- 08 - Encapsulation and Access Modifiers
Source: https://hytalemodding.dev/en/docs/guides/java-basics/08-access-modifiers
Encapsulation is about hiding the internal details of a class and controlling how its data is accessed and modified. This prevents bugs and makes your code more maintainable.
## The Problem Without Encapsulation
```java
public class Player {
public String name;
public int health;
public int maxHealth;
}
public class Main {
public static void main(String[] args) {
Player player = new Player();
player.health = 100;
player.maxHealth = 100;
// Oops! Someone can break the rules
player.health = 500; // Health over maximum!
player.health = -50; // Negative health!
player.name = ""; // Empty name!
}
}
```
Without protection, anyone can set invalid values!
## Access Modifiers
Java has keywords that control who can access your class members:
| Modifier | Class | Package | Subclass | World |
| ----------- | ----- | ------- | -------- | ----- |
| `public` | ✓ | ✓ | ✓ | ✓ |
| `protected` | ✓ | ✓ | ✓ | ✗ |
| (none) | ✓ | ✓ | ✗ | ✗ |
| `private` | ✓ | ✗ | ✗ | ✗ |
**For now, focus on:**
* `public` - Anyone can access
* `private` - Only this class can access
## Making Properties Private
```java
public class Player {
private String name;
private int health;
private int maxHealth;
public Player(String name, int maxHealth) {
this.name = name;
this.health = maxHealth;
this.maxHealth = maxHealth;
}
}
```
Now you can't access properties directly:
```java
Player player = new Player("Alice", 100);
player.health = 500; // ❌ Error! health is private
```
## Getters and Setters
To access private properties, create **getter** and **setter** methods:
```java
public class Player {
private String name;
private int health;
private int maxHealth;
public Player(String name, int maxHealth) {
this.name = name;
this.health = maxHealth;
this.maxHealth = maxHealth;
}
// Getter - returns the value
public int getHealth() {
return health;
}
// Setter - sets the value with validation
public void setHealth(int health) {
if (health < 0) {
this.health = 0;
} else if (health > maxHealth) {
this.health = maxHealth;
} else {
this.health = health;
}
}
public String getName() {
return name;
}
public int getMaxHealth() {
return maxHealth;
}
}
```
Now you can safely interact with the object:
```java
Player player = new Player("Alice", 100);
player.setHealth(150); // Automatically capped at 100
System.out.println(player.getHealth()); // 100
player.setHealth(-20); // Automatically set to 0
System.out.println(player.getHealth()); // 0
```
Follow Java naming conventions:
* **Getter**: `get` + property name (capitalized)
* **Setter**: `set` + property name (capitalized)
* **Boolean**: `is` + property name (capitalized)
```java
private int health;
public int getHealth() { }
public void setHealth(int health) { }
private boolean alive;
public boolean isAlive() { }
public void setAlive(boolean alive) { }
private String name;
public String getName() { }
public void setName(String name) { }
```
## Benefits of Encapsulation
### 1. Validation
```java
public class Item {
private int durability;
private int maxDurability;
public void setDurability(int durability) {
if (durability < 0) {
this.durability = 0;
} else if (durability > maxDurability) {
this.durability = maxDurability;
} else {
this.durability = durability;
}
}
public boolean isBroken() {
return durability <= 0;
}
}
```
### 2. Read-Only Properties
Sometimes you don't want a setter:
```java
public class Monster {
private String id; // Should never change
private int health;
public Monster(String id, int health) {
this.id = id;
this.health = health;
}
// Getter only - no setter!
public String getId() {
return id;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
}
```
### 3. Computed Properties
Getters don't have to return a field directly:
```java
public class Player {
private int health;
private int maxHealth;
public int getHealth() {
return health;
}
// Computed property
public double getHealthPercentage() {
return (health * 100.0) / maxHealth;
}
// Computed property
public boolean isLowHealth() {
return getHealthPercentage() < 25;
}
}
```
## Practical Examples
### Item with Durability
```java
public class Tool {
private String name;
private int durability;
private int maxDurability;
private boolean broken;
public Tool(String name, int maxDurability) {
this.name = name;
this.durability = maxDurability;
this.maxDurability = maxDurability;
this.broken = false;
}
public void use() {
if (broken) {
System.out.println(name + " is broken!");
return;
}
durability--;
System.out.println(name + " used. Durability: " + durability);
if (durability <= 0) {
broken = true;
System.out.println(name + " broke!");
}
}
public void repair() {
durability = maxDurability;
broken = false;
System.out.println(name + " repaired!");
}
// Getters
public String getName() {
return name;
}
public int getDurability() {
return durability;
}
public boolean isBroken() {
return broken;
}
public double getDurabilityPercentage() {
return (durability * 100.0) / maxDurability;
}
}
```
### Bank Account Example
```java
public class PlayerWallet {
private int gold;
private int silver;
public PlayerWallet() {
this.gold = 0;
this.silver = 0;
}
public void addGold(int amount) {
if (amount > 0) {
gold += amount;
System.out.println("Added " + amount + " gold");
}
}
public boolean spendGold(int amount) {
if (amount > gold) {
System.out.println("Not enough gold!");
return false;
}
gold -= amount;
System.out.println("Spent " + amount + " gold");
return true;
}
public int getGold() {
return gold;
}
public int getTotalValue() {
// 1 gold = 100 silver
return gold * 100 + silver;
}
}
```
### Protected Block System
```java
public class ProtectedBlock {
private int x, y, z;
private String type;
private String owner;
private boolean locked;
public ProtectedBlock(int x, int y, int z, String type, String owner) {
this.x = x;
this.y = y;
this.z = z;
this.type = type;
this.owner = owner;
this.locked = true;
}
public boolean canBreak(String playerName) {
if (!locked) {
return true;
}
return playerName.equals(owner);
}
public void unlock(String playerName) {
if (playerName.equals(owner)) {
locked = false;
System.out.println("Block unlocked");
} else {
System.out.println("You don't own this block!");
}
}
// Getters only - position and owner shouldn't change
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
public String getOwner() {
return owner;
}
public boolean isLocked() {
return locked;
}
}
```
## When to Use Private vs Public
**Make it private by default!** Only make things public if they need to be accessed from outside.
**Private:**
* Internal data (health, position, inventory)
* Helper methods used only within the class
* Anything that needs validation
**Public:**
* Methods that define the class's behavior
* Constructor
* Methods other classes need to call
```java
public class Example {
// Private - internal data
private int internalCounter;
private String secretKey;
// Public - part of the interface
public void doSomething() {
// Uses private helper method
validateData();
}
// Private - internal helper
private void validateData() {
// ...
}
}
```
## The `final` Keyword
`final` means a variable can't be changed after it's set:
```java
public class Player {
private final String id; // Can't change after creation
private String name; // Can change
private int health; // Can change
public Player(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
// No setId() - it's final!
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
```
## The `static` Keyword
### Static Members
A class can define two kinds of members:
* **Instance members** — owned by each object (each instance has its own copy).
* **Static members** — owned by the class (one shared copy for the entire type).
Put simply: instance members belong to objects; static members belong to the class itself and are shared by all objects of that type.
#### Declaration
```java
/* (access modifier) */ static ... memberName;
```
#### Example
```java
class Data {
public int x; // Instanced member
public static int y = 1000; // Static member
// Instanced member:
// can access to both static and non-static members
public void foo() {
x = 100; // OK - same as this.x = 100;
y = 100; // OK - same as Data.y = 200;
}
// Static member:
// cannot access to non-static variables
public static void bar() {
x = 100; // Error: non-static variable x cannot be referenced from a static context
y = 100; // OK
}
}
```
#### Accessing static members
```java
Data data = new Data();
data.x = 1000; // OK
data.y = 1000; // OK-ish - not really suggested; it's better to use Data.y
Data.y = 1000; // OK - best practice
Data.x = 1000; // Error: cannot access instanced variables in a static context
```
### Static Fields
A static field represents a data member owned by the class type rather than the object. Static fields are also stored in a specific memory location that's been shared between all the object instances that are created.
It is declared as following:
```java
/* (access modifier) (optional) */ static /* final/volatile (optional) */ fieldName;
```
Let's take the same Data class example and add this constructor:
```java
public Data() {
y++; // remember that's the same as Data.y++;
}
```
```java
// Every instance of Data will have a private copy of the instanced member x
// However it will point to the same location in memory for the member y
Data d1 = new Data(); // y = 1001
d1.x = 5;
Data d2 = new Data(); // y = 1002
d2.x = 25;
Data d3 = new Data(); // y = 1003
// ... and so on
```
### Static Methods
Static methods essentially represent a function member of a certain class type
From the Data class remember the function (instanced method) `foo` and (static method) `bar`
One can access those methods via:
```java
Data d1 = new Data();
d1.foo(); // Instanced method: Accessible ONLY by an object
Data.bar(); // Static method: accessible without an object
```
### Static Initializer
Use a *static initializer* block to run initialization logic when the class is first loaded:
```java
class OtherData {
private static int a = 12;
private static int b;
private static String msg;
static {
msg = "Initialization..."
System.out.println(msg);
b = 4;
// ... complex initialization that can't be done in a single expression
}
}
```
## Practice Exercises
1. **Create a `BankAccount` Class**:
* Private properties: accountNumber, balance
* Constructor to set account number
* Methods: deposit(), withdraw(), getBalance()
* Validation: can't withdraw more than balance
* Account number should be read-only
2. **Create a `Door` Class**:
* Private properties: isLocked, keyCode
* Constructor to set the key code
* Methods: lock(), unlock(String code), isLocked()
* unlock() only works with correct code
* Code should be private (don't expose it!)
3. **Create a `PlayerStats` Class**:
* Private properties: strength, defense, speed
* Constructor to set all stats
* Getters for all stats
* Method: getPowerLevel() that returns strength + defense + speed
* Stats can't be negative or over 100
4. **Refactor a Class**: Take one of your classes from the previous lesson and add proper encapsulation:
* Make all properties private
* Add appropriate getters and setters
* Add validation where needed
--- 04 - Control Flow Loops
Source: https://hytalemodding.dev/en/docs/guides/java-basics/04-control-flow-loops
Loops let you repeat code without writing it over and over. They're essential for processing collections, generating worlds, and updating game objects.
## While Loop
Repeats code while a condition is true:
Code
Output
```java
int countdown = 5;
while (countdown > 0) {
System.out.println(countdown);
countdown--;
}
System.out.println("Blast off!");
```
```java
5
4
3
2
1
Blast off!
```
### Structure
```java
while (condition) {
// code to repeat
}
```
Always make sure your loop condition will eventually become false, or your program will run forever!
Bad Example
Good Example
```java
int x = 0;
while (x < 10) {
System.out.println("Stuck!");
// Forgot to increase x!
}
```
```java
int x = 0;
while (x < 10) {
System.out.println("Counting: " + x);
x++; // This increases x! // [!code ++]
}
```
## Do-While Loop
Like a while loop, but always runs at least once:
```java
int health = 0;
do {
System.out.println("Attempting respawn...");
health = 100;
} while (health <= 0);
```
## For Loop
Best when you know how many times to repeat:
Code
Output
```java
for (int i = 1; i <= 5; i++) {
System.out.println("Level " + i);
}
```
```java
Level 1
Level 2
Level 3
Level 4
Level 5
```
### Structure
```java
for (initialization; condition; update) {
// code to repeat
}
```
```java
for (int i = 0; i < 10; i++) {
// ^ ^ ^
// Start Stop Step
}
```
* **Initialization**: int i = 0 - Runs once at the start
* **Condition**: i \< 10 - Checked before each loop
* **Update**: i++ - Runs after each loop
## Break Statement
You can exit a loop early with `break`:
```java
for (int i = 1; i <= 10; i++) {
if (i == 6) {
break; // Exit loop when i is 6
}
System.out.println(i);
}
```
## Continue Statement
Skip to the next iteration with `continue`:
```java
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
System.out.println(i); // Prints only odd numbers
}
```
## Nested Loops
You can put loops inside other loops:
```java
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 2; j++) {
System.out.println("i: " + i + ", j: " + j);
}
}
```
Be careful with nested loops! They can slow down your mod significantly.
```java
// This runs 1,000,000 times!
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 1000; j++) {
// Code here
}
}
```
Try to avoid deeply nested loops when processing large amounts of data.
--- 02 - Operators and Expressions
Source: https://hytalemodding.dev/en/docs/guides/java-basics/02-operators
Operators are symbols that perform operations on variables and values. They're the verbs of programming - they make things happen!
## Arithmetic Operators
Used for mathematical calculations:
```java
int a = 10;
int b = 5;
int sum = a + b; // Addition: 15
int difference = a - b; // Subtraction: 5
int product = a * b; // Multiplication: 50
int quotient = a / b; // Division: 2
int remainder = a % b; // Modulus (remainder): 0
```
When dividing integers, Java performs integer division, which means it discards any decimal part. For example, `7 / 2` would result in `3`, not `3.5`.
```java
int x = 7 / 2; // 3 (not 3.5!) - Integer division
double y = 7.0 / 2; // 3.5 - At least one double gives double result
double z = (double) 7 / 2; // 3.5 - Casting works too
```
### Modulus (%) - The remainder Operator
The modulus operator returns the remainder of a division operation:
```java
int remainder = 10 % 3; // 1 (because 10 divided by 3 is 3 with a remainder of 1)
```
## Compound Assignment Operators
These operators combine an arithmetic operation with assignment:
```java
int a = 10;
a = a + 5; // The old way
a += 5; // a = a + 5
a -= 3; // a = a - 3
a *= 2; // a = a * 2
a /= 4; // a = a / 4
a %= 3; // a = a % 3
```
## Increment and Decrement Operators
Used to increase or decrease a variable's value by 1:
```java
int a = 5;
a++; // Increment: a becomes 6
a--; // Decrement: a becomes 5 again
```
```java
int a = 5;
int b = a++; // b is 5, a is now 6
int c = ++a; // a is 7, c is now 7
```
## Comparison Operators
Used to compare two values, returning a boolean result (`true` or `false`):
```java
int a = 10;
int b = 5;
a == b // false - Equal to
a != b // true - Not equal to
a > b // true - Greater than
a < b // false - Less than
a >= b // true - Greater than or equal to
a <= b // false - Less than or equal to
```
## Logical Operators
Used to combine multiple boolean expressions:
```java
boolean x = true;
boolean y = false;
x && y // false - Logical AND
x || y // true - Logical OR
!x // false - Logical NOT
```
### Combining Logical Operators
```java
boolean result = (a > b) && (b < 10); // true if both conditions are true
```
## String Concatenation
The `+` operator can also be used to concatenate (join) strings:
```java
String greeting = "Hello, " + "world!"; // "Hello, world!"
String name = "Alice";
String greetingWithName = "Hello, " + name + "!"; // "Hello, Alice!"
```
## Operator Precedence
Operator Precedence means which operator gets evaluated first in an line of code. Think of it like BODMAS/PEMDAS in math!
```java
int result = 10 + 5 * 2; // result is 20, not 30
```
{/* practice to be added */}
--- 13 - Inheritance
Source: https://hytalemodding.dev/en/docs/guides/java-basics/13-inheritance
Inheritance lets you create new classes based on existing ones. The new class inherits all the properties and methods of the parent class, and can add its own or modify inherited ones.
## What is Inheritance?
Think of inheritance like a family tree. A child inherits traits from their parent, but can also have their own unique traits.
```java
// Parent class (superclass)
public class Entity {
protected String name;
protected int health;
public Entity(String name, int health) {
this.name = name;
this.health = health;
}
public void takeDamage(int damage) {
health -= damage;
System.out.println(name + " took " + damage + " damage!");
}
}
// Child class (subclass)
public class Player extends Entity {
private int level;
public Player(String name, int health, int level) {
super(name, health); // Call parent constructor
this.level = level;
}
public void levelUp() {
level++;
System.out.println(name + " leveled up to " + level + "!");
}
}
```
* **Superclass/Parent**: The class being inherited from (Entity)
* **Subclass/Child**: The class inheriting (Player)
* **extends**: Keyword to inherit from a class
* **super**: Keyword to access parent class members
```java
public class Monster extends Entity {
// Monster IS-A Entity
// Monster inherits from Entity
// Entity is the parent, Monster is the child
}
```
## The extends Keyword
Use `extends` to inherit from a class:
```java
public class Animal {
protected String name;
public void makeSound() {
System.out.println(name + " makes a sound");
}
}
public class Dog extends Animal {
public void wagTail() {
System.out.println(name + " wags tail");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "Buddy";
dog.makeSound(); // Inherited from Animal
dog.wagTail(); // Dog's own method
}
}
```
## The super Keyword
`super` refers to the parent class:
### Calling Parent Constructor
```java
public class Entity {
protected String name;
public Entity(String name) {
this.name = name;
}
}
public class Player extends Entity {
private int level;
public Player(String name, int level) {
super(name); // Call parent constructor FIRST
this.level = level;
}
}
```
* `super()` must be the **first** statement in child constructor
* If you don't call `super()`, Java automatically calls the no-argument parent constructor
* If parent has no no-argument constructor, you MUST call `super()` with arguments
```java
// Wrong - super() not first
public Player(String name, int level) {
this.level = level;
super(name); // Error!
}
// Correct
public Player(String name, int level) {
super(name); // First statement
this.level = level;
}
```
### Calling Parent Methods
```java
public class Entity {
protected int health;
public void takeDamage(int damage) {
health -= damage;
System.out.println("Entity took damage!");
}
}
public class Player extends Entity {
@Override
public void takeDamage(int damage) {
super.takeDamage(damage); // Call parent version
if (health < 20) {
System.out.println("Warning: Low health!");
}
}
}
```
## Method Overriding
Child classes can replace parent methods:
```java
public class Entity {
public void attack() {
System.out.println("Entity attacks!");
}
}
public class Player extends Entity {
@Override // Good practice to use this annotation
public void attack() {
System.out.println("Player swings sword!");
}
}
public class Monster extends Entity {
@Override
public void attack() {
System.out.println("Monster bites!");
}
}
public class Main {
public static void main(String[] args) {
Player player = new Player();
Monster monster = new Monster();
player.attack(); // "Player swings sword!"
monster.attack(); // "Monster bites!"
}
}
```
Always use `@Override` when overriding methods:
* Helps catch typos (if method doesn't exist in parent, you get an error)
* Makes code clearer
* Good documentation
```java
// Without @Override - typo not caught
public void attac() { // Typo! Creates new method instead
// ...
}
// With @Override - error caught immediately
@Override
public void attac() { // Error: method doesn't exist in parent
// ...
}
```
## Access Modifiers in Inheritance
* `public` - Accessible everywhere
* `protected` - Accessible in class and subclasses
* `private` - Only in the class (not inherited)
```java
public class Parent {
public int publicVar; // Child can access
protected int protectedVar; // Child can access
private int privateVar; // Child CANNOT access
private void privateMethod() {
// Child cannot call this
}
protected void protectedMethod() {
// Child can call this
}
}
public class Child extends Parent {
public void test() {
publicVar = 10; // OK
protectedVar = 20; // OK
privateVar = 30; // Error!
protectedMethod(); // OK
privateMethod(); // Error!
}
}
```
## Practical Examples
### Game Entity Hierarchy
```java
// Base class for all entities
public class Entity {
protected String name;
protected int health;
protected int maxHealth;
protected double x, y, z;
public Entity(String name, int maxHealth) {
this.name = name;
this.health = maxHealth;
this.maxHealth = maxHealth;
}
public void takeDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
System.out.println(name + " took " + damage + " damage. Health: " + health);
}
public boolean isAlive() {
return health > 0;
}
public void moveTo(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
System.out.println(name + " moved to (" + x + ", " + y + ", " + z + ")");
}
}
// Player extends Entity
public class Player extends Entity {
private int level;
private int experience;
private int mana;
public Player(String name) {
super(name, 100);
this.level = 1;
this.experience = 0;
this.mana = 50;
}
public void gainExperience(int amount) {
experience += amount;
System.out.println("Gained " + amount + " XP");
if (experience >= level * 100) {
levelUp();
}
}
private void levelUp() {
level++;
maxHealth += 10;
health = maxHealth;
mana += 5;
System.out.println("Level up! Now level " + level);
}
@Override
public void takeDamage(int damage) {
super.takeDamage(damage);
if (health < maxHealth * 0.25) {
System.out.println("⚠ WARNING: Low health!");
}
}
}
// Monster extends Entity
public class Monster extends Entity {
private int attackPower;
private String type;
public Monster(String name, int health, int attackPower, String type) {
super(name, health);
this.attackPower = attackPower;
this.type = type;
}
public int attack() {
System.out.println(name + " attacks!");
return attackPower;
}
@Override
public void takeDamage(int damage) {
super.takeDamage(damage);
if (!isAlive()) {
System.out.println(name + " was defeated!");
}
}
}
// Boss extends Monster (multi-level inheritance)
public class Boss extends Monster {
private int phase;
public Boss(String name, int health, int attackPower) {
super(name, health, attackPower, "Boss");
this.phase = 1;
}
@Override
public void takeDamage(int damage) {
super.takeDamage(damage);
// Change phase at 50% health
if (phase == 1 && health < maxHealth / 2) {
phase = 2;
System.out.println(name + " enters PHASE 2!");
}
}
@Override
public int attack() {
int damage = super.attack();
if (phase == 2) {
damage *= 2;
System.out.println("ENRAGED ATTACK!");
}
return damage;
}
}
```
### Item Hierarchy
```java
// Base Item class
public class Item {
protected String name;
protected int value;
protected double weight;
public Item(String name, int value, double weight) {
this.name = name;
this.value = value;
this.weight = weight;
}
public void use() {
System.out.println("Using " + name);
}
public String getInfo() {
return name + " ($" + value + ", " + weight + " kg)";
}
}
// Weapon extends Item
public class Weapon extends Item {
private int damage;
private int durability;
public Weapon(String name, int value, double weight, int damage, int durability) {
super(name, value, weight);
this.damage = damage;
this.durability = durability;
}
@Override
public void use() {
if (durability > 0) {
System.out.println("Attacking with " + name + " for " + damage + " damage!");
durability--;
} else {
System.out.println(name + " is broken!");
}
}
@Override
public String getInfo() {
return super.getInfo() + ", Damage: " + damage + ", Durability: " + durability;
}
}
// Consumable extends Item
public class Consumable extends Item {
private int healAmount;
private int uses;
public Consumable(String name, int value, double weight, int healAmount, int uses) {
super(name, value, weight);
this.healAmount = healAmount;
this.uses = uses;
}
@Override
public void use() {
if (uses > 0) {
System.out.println("Used " + name + ", restored " + healAmount + " health!");
uses--;
} else {
System.out.println("No " + name + " left!");
}
}
@Override
public String getInfo() {
return super.getInfo() + ", Heals: " + healAmount + ", Uses: " + uses;
}
}
// Armor extends Item
public class Armor extends Item {
private int defense;
private String slot;
public Armor(String name, int value, double weight, int defense, String slot) {
super(name, value, weight);
this.defense = defense;
this.slot = slot;
}
@Override
public void use() {
System.out.println("Equipped " + name + " (+" + defense + " defense)");
}
@Override
public String getInfo() {
return super.getInfo() + ", Defense: " + defense + ", Slot: " + slot;
}
}
```
## Polymorphism
Child objects can be treated as parent objects:
```java
Entity entity1 = new Player("Alice");
Entity entity2 = new Monster("Goblin", 50, 10, "Hostile");
Entity entity3 = new Boss("Dragon", 500, 50);
// All can use Entity methods
entity1.takeDamage(10);
entity2.takeDamage(10);
entity3.takeDamage(10);
// Array of different types
Entity[] entities = {
new Player("Bob"),
new Monster("Zombie", 30, 8, "Hostile"),
new Monster("Spider", 20, 5, "Hostile")
};
// Process all entities the same way
for (Entity entity : entities) {
entity.takeDamage(5);
}
```
Polymorphism lets you write code that works with parent types but handles child types correctly:
```java
public void damageEntity(Entity entity, int damage) {
entity.takeDamage(damage);
// Works for Player, Monster, Boss, etc.
// Each uses their own version of takeDamage()
}
// Can call with any Entity type
damageEntity(new Player("Alice"), 10);
damageEntity(new Monster("Goblin", 50, 10, "Hostile"), 10);
damageEntity(new Boss("Dragon", 500, 50), 10);
```
## The Object Class
All classes in Java automatically inherit from `Object`:
```java
public class MyClass {
// Automatically extends Object
// Has methods like toString(), equals(), etc.
}
```
Common Object methods to override:
```java
public class Player {
private String name;
private int level;
@Override
public String toString() {
return "Player: " + name + " (Lv. " + level + ")";
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Player) {
Player other = (Player) obj;
return this.name.equals(other.name);
}
return false;
}
}
```
## Final Classes and Methods
`final` prevents inheritance or overriding:
```java
// Final class - cannot be extended
public final class SpecialItem {
// No class can extend this
}
// Final method - cannot be overridden
public class Entity {
public final void printName() {
System.out.println(name);
}
}
public class Player extends Entity {
@Override
public void printName() { // Error! Method is final
// Cannot override
}
}
```
## Common Mistakes
```java
// Wrong - Forgetting super()
public class Player extends Entity {
public Player(String name) {
// Error! Entity has no no-arg constructor
}
}
// Correct
public class Player extends Entity {
public Player(String name) {
super(name, 100); // Call parent constructor
}
}
// Wrong - Accessing private members
public class Child extends Parent {
public void test() {
privateVar = 10; // Error! private is not inherited
}
}
// Correct - Use protected
public class Parent {
protected int protectedVar; // Child can access
}
// Wrong - Multiple inheritance (not allowed in Java)
public class Child extends Parent1, Parent2 { // Error!
}
// Correct - Single inheritance only
public class Child extends Parent {
}
```
## Practice Exercises
1. **Vehicle Hierarchy**: Create a `Vehicle` class with properties like speed and fuel. Create `Car` and `Motorcycle` subclasses with their own unique features.
2. **Shape Calculator**: Create a `Shape` class with a `calculateArea()` method. Create `Circle`, `Rectangle`, and `Triangle` subclasses that override this method.
3. **RPG Characters**: Create a `Character` class. Extend it to make `Warrior`, `Mage`, and `Archer` classes with unique abilities.
4. **Animal Sounds**: Create an `Animal` class with a `makeSound()` method. Create various animal subclasses that override this method.
--- 10 - ArrayList and Collections
Source: https://hytalemodding.dev/en/docs/guides/java-basics/10-arraylist
Arrays have a fixed size, but what if you need to add or remove elements? That's where `ArrayList` comes in - a resizable collection that grows and shrinks automatically.
## ArrayList Basics
```java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// Create an ArrayList of Strings
ArrayList players = new ArrayList<>();
// Add elements
players.add("Alice");
players.add("Bob");
players.add("Charlie");
System.out.println(players); // [Alice, Bob, Charlie]
}
}
```
| **Array** | **ArrayList** |
| --------------------------------------- | ----------------------------------------------------- |
| Fixed size | Resizable (grows/shrinks) |
| Can hold primitives (int, double, etc.) | Can only hold objects (Integer, Double, String, etc.) |
| Faster access | More features (add, remove, etc.) |
| `int[] numbers = new int[10];` | `ArrayList numbers = new ArrayList<>();` |
## Creating ArrayLists
```java
import java.util.ArrayList;
// Empty ArrayList
ArrayList items = new ArrayList<>();
// With initial values (Java 9+)
ArrayList colors = new ArrayList<>(List.of("Red", "Green", "Blue"));
// Different types
ArrayList numbers = new ArrayList<>();
ArrayList prices = new ArrayList<>();
ArrayList flags = new ArrayList<>();
```
You can't use primitives directly. Use their wrapper classes:
```java
// Wrong
ArrayList numbers = new ArrayList<>();
// Correct - Use wrapper class
ArrayList numbers = new ArrayList<>();
```
Wrapper classes:
* `int` → `Integer`
* `double` → `Double`
* `boolean` → `Boolean`
* `char` → `Character`
## Common ArrayList Methods
### Adding Elements
```java
ArrayList items = new ArrayList<>();
items.add("Sword"); // Add to end
items.add("Shield");
items.add(0, "Helmet"); // Add at specific index
System.out.println(items); // [Helmet, Sword, Shield]
```
### Accessing Elements
```java
ArrayList items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
String first = items.get(0); // "Sword"
String second = items.get(1); // "Shield"
int size = items.size(); // 2
```
### Removing Elements
```java
ArrayList items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
items.add("Potion");
items.remove(1); // Remove by index (Shield)
items.remove("Potion"); // Remove by value
System.out.println(items); // [Sword]
```
### Checking Contents
```java
ArrayList items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
boolean hasSword = items.contains("Sword"); // true
boolean hasAxe = items.contains("Axe"); // false
boolean isEmpty = items.isEmpty(); // false
int size = items.size(); // 2
```
### Modifying Elements
```java
ArrayList items = new ArrayList<>();
items.add("Wooden Sword");
items.set(0, "Iron Sword"); // Replace at index 0
System.out.println(items); // [Iron Sword]
```
### Clearing All Elements
```java
ArrayList items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
items.clear(); // Remove everything
System.out.println(items); // []
```
## Looping Through ArrayList
### Using For Loop
```java
ArrayList players = new ArrayList<>();
players.add("Alice");
players.add("Bob");
players.add("Charlie");
for (int i = 0; i < players.size(); i++) {
System.out.println(players.get(i));
}
```
### Using Enhanced For Loop
```java
for (String player : players) {
System.out.println(player);
}
```
### Using forEach (Java 8+)
```java
players.forEach(player -> System.out.println(player));
```
## Practical Examples
### Player Inventory System
```java
import java.util.ArrayList;
public class Inventory {
private ArrayList items;
private int maxSize;
public Inventory(int maxSize) {
this.items = new ArrayList<>();
this.maxSize = maxSize;
}
public boolean addItem(String item) {
if (items.size() >= maxSize) {
System.out.println("Inventory full!");
return false;
}
items.add(item);
System.out.println("Added: " + item);
return true;
}
public boolean removeItem(String item) {
if (items.remove(item)) {
System.out.println("Removed: " + item);
return true;
}
System.out.println("Item not found!");
return false;
}
public void displayInventory() {
System.out.println("=== Inventory (" + items.size() + "/" + maxSize + ") ===");
for (int i = 0; i < items.size(); i++) {
System.out.println((i + 1) + ". " + items.get(i));
}
}
public int getItemCount() {
return items.size();
}
public boolean isFull() {
return items.size() >= maxSize;
}
}
// Usage
public class Main {
public static void main(String[] args) {
Inventory inv = new Inventory(5);
inv.addItem("Sword");
inv.addItem("Shield");
inv.addItem("Potion");
inv.displayInventory();
inv.removeItem("Shield");
inv.displayInventory();
}
}
```
### Quest System
```java
import java.util.ArrayList;
public class QuestManager {
private ArrayList activeQuests;
private ArrayList completedQuests;
public QuestManager() {
this.activeQuests = new ArrayList<>();
this.completedQuests = new ArrayList<>();
}
public void startQuest(String questName) {
if (!activeQuests.contains(questName)) {
activeQuests.add(questName);
System.out.println("Quest started: " + questName);
} else {
System.out.println("Quest already active!");
}
}
public void completeQuest(String questName) {
if (activeQuests.remove(questName)) {
completedQuests.add(questName);
System.out.println("Quest completed: " + questName);
} else {
System.out.println("Quest not found in active quests!");
}
}
public void displayQuests() {
System.out.println("\n=== Active Quests ===");
if (activeQuests.isEmpty()) {
System.out.println("No active quests");
} else {
for (String quest : activeQuests) {
System.out.println("- " + quest);
}
}
System.out.println("\n=== Completed Quests ===");
if (completedQuests.isEmpty()) {
System.out.println("No completed quests");
} else {
for (String quest : completedQuests) {
System.out.println("" + quest);
}
}
}
public int getTotalCompleted() {
return completedQuests.size();
}
}
```
### Leaderboard System
```java
import java.util.ArrayList;
import java.util.Collections;
public class Leaderboard {
private ArrayList scores;
private ArrayList players;
public Leaderboard() {
this.scores = new ArrayList<>();
this.players = new ArrayList<>();
}
public void addScore(String player, int score) {
players.add(player);
scores.add(score);
System.out.println(player + " scored " + score + " points!");
}
public void displayTop(int count) {
System.out.println("\n=== Top " + count + " Players ===");
// Create copies to sort
ArrayList sortedScores = new ArrayList<>(scores);
ArrayList sortedPlayers = new ArrayList<>(players);
// Bubble sort (simple for learning)
for (int i = 0; i < sortedScores.size() - 1; i++) {
for (int j = 0; j < sortedScores.size() - i - 1; j++) {
if (sortedScores.get(j) < sortedScores.get(j + 1)) {
// Swap scores
int tempScore = sortedScores.get(j);
sortedScores.set(j, sortedScores.get(j + 1));
sortedScores.set(j + 1, tempScore);
// Swap players
String tempPlayer = sortedPlayers.get(j);
sortedPlayers.set(j, sortedPlayers.get(j + 1));
sortedPlayers.set(j + 1, tempPlayer);
}
}
}
// Display top scores
int limit = Math.min(count, sortedScores.size());
for (int i = 0; i < limit; i++) {
System.out.println((i + 1) + ". " + sortedPlayers.get(i) +
" - " + sortedScores.get(i) + " points");
}
}
}
```
### Online Players Tracker
```java
import java.util.ArrayList;
public class OnlinePlayersTracker {
private ArrayList onlinePlayers;
public OnlinePlayersTracker() {
this.onlinePlayers = new ArrayList<>();
}
public void playerJoin(String playerName) {
if (!onlinePlayers.contains(playerName)) {
onlinePlayers.add(playerName);
broadcast(playerName + " joined the game");
updatePlayerCount();
}
}
public void playerLeave(String playerName) {
if (onlinePlayers.remove(playerName)) {
broadcast(playerName + " left the game");
updatePlayerCount();
}
}
public boolean isOnline(String playerName) {
return onlinePlayers.contains(playerName);
}
public int getPlayerCount() {
return onlinePlayers.size();
}
public void listPlayers() {
System.out.println("\n=== Online Players (" + getPlayerCount() + ") ===");
for (String player : onlinePlayers) {
System.out.println("• " + player);
}
}
private void broadcast(String message) {
System.out.println("[Server] " + message);
}
private void updatePlayerCount() {
System.out.println("Players online: " + getPlayerCount());
}
}
```
## ArrayList of Custom Objects
You can store your own classes in ArrayLists:
```java
public class Player {
private String name;
private int level;
public Player(String name, int level) {
this.name = name;
this.level = level;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
@Override
public String toString() {
return name + " (Lv. " + level + ")";
}
}
public class Main {
public static void main(String[] args) {
ArrayList players = new ArrayList<>();
players.add(new Player("Alice", 10));
players.add(new Player("Bob", 15));
players.add(new Player("Charlie", 8));
// Display all players
for (Player player : players) {
System.out.println(player);
}
// Find player by name
for (Player player : players) {
if (player.getName().equals("Bob")) {
System.out.println("Found: " + player);
}
}
}
}
```
## Useful ArrayList Operations
### Convert to Array
```java
ArrayList list = new ArrayList<>();
list.add("A");
list.add("B");
String[] array = list.toArray(new String[0]);
```
### Convert Array to ArrayList
```java
String[] array = {"A", "B", "C"};
ArrayList list = new ArrayList<>(Arrays.asList(array));
```
### Copy an ArrayList
```java
ArrayList original = new ArrayList<>();
original.add("A");
original.add("B");
// Shallow copy
ArrayList copy = new ArrayList<>(original);
```
### Find Index of Element
```java
ArrayList items = new ArrayList<>();
items.add("Sword");
items.add("Shield");
int index = items.indexOf("Shield"); // 1
int notFound = items.indexOf("Axe"); // -1
```
## Practice Exercises
1. **Shopping Cart**: Create a shopping cart system:
* Add items
* Remove items
* Calculate total items
* Clear cart
2. **Friend List**: Create a friend management system:
* Add friend
* Remove friend
* Check if someone is a friend
* Display all friends
3. **Todo List**: Build a todo list:
* Add task
* Mark task as complete (move to completed list)
* Display active tasks
* Display completed tasks
4. **High Scores**: Create a high score system:
* Add scores
* Display top 5 scores in order
* Remove lowest score if more than 10 scores exist
***
--- 01 - Variables & Data Types
Source: https://hytalemodding.dev/en/docs/guides/java-basics/01-variables
Variables are containers that store data in your program. Think of them as labeled boxes where you can put different types of information.
## What is a Variable?
A variable has three key components:
1. **Type**: What kind of data it holds (e.g., integer, string, boolean)
2. **Name**: The label you use to refer to the variable
3. **Value**: The data it contains
## Declaring/Creating Variables
In Java, you must declare a variable before using it, like you'd need a box before putting something in it. The syntax is:
```java
int age; // A declaration
age = 18; // An assignment
int age = 18; // Declaration and assignment in one line, also considered an initialization
```
## Data Types
You might wonder what "int" means. It's a primitive data type in Java. Java has 8 primitive and non-primitive (reference) data types.
You will learn the difference between them later. Here are the most important ones for modding:
| Data Type | Description | Example |
| --------- | ----------------------------- | -------------------------- |
| int | Integer (whole numbers) | `int score = 100;` |
| double | Decimal numbers | `double pi = 3.14;` |
| boolean | True or false values | `boolean isActive = true;` |
| char | Single character | `char grade = 'A';` |
| String | Sequence of characters (text) | `String name = "Hytale";` |
## Type Conversion
Sometimes you might not want a variable to stay the same type. You can convert between types:
### Automatic
```java
int num = 10;
double decimalNum = num; // 10.0 - works automatically
```
### Manual (Casting)
```java
double decimalNum = 9.78;
int num = (int) decimalNum; // 9 - decimals are dropped
```
## Constants
If you want a variable that shouldn't change, use the `final` keyword:
```java
final double PI = 3.14159;
```
**Convention**: Constants use ALL\_CAPS\_WITH\_UNDERSCORES.
## Practice
It's best to practice what you just learnt - don't look above now and try to do this yourself. If you get stuck, look back up!
1. Declare an integer variable named `lives` and set it to 3.
2. Declare a double variable named `gravity` and set it to 9.81.
3. Declare a boolean variable named `isGameOver` and set it to false.
4. Convert the `lives` variable to a double and store it in a new variable named `livesAsDouble`.
5. Declare a constant named `MAX_LEVEL` and set it to 100.
Great! You've learned about variables and data types in Java. Now we're going to learn about operators and expressions in the next lesson.
--- 09 - Working with Strings
Source: https://hytalemodding.dev/en/docs/guides/java-basics/09-string
Strings are one of the most commonly used types in Java. They're essential for handling player names, chat messages, item descriptions, and like everything you see.
## String Basics
Strings are objects that store text:
```java
String name = "Simon";
String message = "Welcome to Hytale!";
String empty = "";
```
Once created, a String **cannot be changed**. Methods that seem to modify a string actually create a new one!
```java
String text = "Hello";
text.toUpperCase(); // Creates "HELLO" but doesn't save it
System.out.println(text); // Still "Hello"
String upper = text.toUpperCase(); // ✓ Save the result
System.out.println(upper); // "HELLO"
```
## String Methods
### Length
```java
String name = "Alice";
int length = name.length();
System.out.println(length); // 5
```
### Changing Case
```java
String text = "Hello World";
String upper = text.toUpperCase(); // "HELLO WORLD"
String lower = text.toLowerCase(); // "hello world"
```
### Checking Contents
```java
String message = "Welcome to Hytale";
boolean hasHytale = message.contains("Hytale"); // true
boolean hasMinecraft = message.contains("Minecraft"); // false
boolean starts = message.startsWith("Welcome"); // true
boolean ends = message.endsWith("Hytale"); // true
```
### Extracting Parts
```java
String text = "Hello World";
char first = text.charAt(0); // 'H'
char last = text.charAt(10); // 'd'
String sub1 = text.substring(0, 5); // "Hello"
String sub2 = text.substring(6); // "World"
```
`substring(start, end)` includes `start` but excludes `end`:
```java
String text = "Hytale";
// 012345 (indices)
text.substring(0, 2); // "Hy" (indices 0 and 1)
text.substring(2, 6); // "tale" (indices 2, 3, 4, 5)
text.substring(2); // "tale" (from 2 to end)
```
### Replacing Text
```java
String text = "I love Minecraft";
String replaced = text.replace("Minecraft", "Hytale");
System.out.println(replaced); // "I love Hytale"
String noSpaces = "Hello World".replace(" ", "");
System.out.println(noSpaces); // "HelloWorld"
```
### Trimming Whitespace
```java
String messy = " Hello World ";
String clean = messy.trim();
System.out.println(clean); // "Hello World" (no spaces at ends)
```
### Splitting Strings
```java
String command = "give player sword 5";
String[] parts = command.split(" ");
System.out.println(parts[0]); // "give"
System.out.println(parts[1]); // "player"
System.out.println(parts[2]); // "sword"
System.out.println(parts[3]); // "5"
```
## String Comparison
Always use `.equals()` to compare string contents!
```java
String name1 = "Steve";
String name2 = "Steve";
// Wrong - Compares object references
if (name1 == name2) {
System.out.println("Same");
}
// Correct - Compares actual text
if (name1.equals(name2)) {
System.out.println("Same");
}
// Ignore case when comparing
if (name1.equalsIgnoreCase("steve")) {
System.out.println("Same (ignoring case)");
}
```
### Comparing Order
```java
String a = "Apple";
String b = "Banana";
int result = a.compareTo(b);
// result < 0 if a comes before b
// result == 0 if a equals b
// result > 0 if a comes after b
if (a.compareTo(b) < 0) {
System.out.println(a + " comes before " + b);
}
```
## String Concatenation
### Using + Operator
```java
String first = "Hello";
String second = "World";
String combined = first + " " + second; // "Hello World"
int score = 100;
String message = "Score: " + score; // "Score: 100"
```
### Using concat()
```java
String result = "Hello".concat(" World"); // "Hello World"
```
### Building Long Strings
For many concatenations, use `StringBuilder`:
```java
StringBuilder builder = new StringBuilder();
builder.append("Player: ");
builder.append("Alice");
builder.append(", Level: ");
builder.append(10);
builder.append(", Health: ");
builder.append(100);
String result = builder.toString();
System.out.println(result);
// "Player: Alice, Level: 10, Health: 100"
```
Regular concatenation creates many temporary strings:
```java
// Inefficient - Creates many temporary strings
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i; // Creates 1000 temporary strings!
}
// Efficient - StringBuilder is mutable
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // Modifies one object
}
String result = sb.toString();
```
Use `StringBuilder` when building strings in loops!
## Practical Examples
### Parse Player Command
```java
public class CommandParser {
public static void parseCommand(String command) {
// "/give Steve diamond_sword 1"
String[] parts = command.split(" ");
String action = parts[0].replace("/", ""); // "give"
String player = parts[1]; // "Steve"
String item = parts[2]; // "diamond_sword"
int amount = Integer.parseInt(parts[3]); // 1
System.out.println("Action: " + action);
System.out.println("Player: " + player);
System.out.println("Item: " + item);
System.out.println("Amount: " + amount);
}
public static void main(String[] args) {
parseCommand("/give Steve diamond_sword 1");
}
}
```
### Format Player Display Name
```java
public class PlayerFormatter {
public static String formatName(String name, int level, String rank) {
StringBuilder formatted = new StringBuilder();
if (rank != null && !rank.isEmpty()) {
formatted.append("[").append(rank).append("] ");
}
formatted.append(name);
formatted.append(" (Lv. ").append(level).append(")");
return formatted.toString();
}
public static void main(String[] args) {
String display1 = formatName("Alice", 10, "VIP");
System.out.println(display1); // "[VIP] Alice (Lv. 10)"
String display2 = formatName("Bob", 5, null);
System.out.println(display2); // "Bob (Lv. 5)"
}
}
```
### Validate Username
```java
public class UsernameValidator {
public static boolean isValid(String username) {
// Rules: 3-16 characters, letters and numbers only
if (username == null || username.isEmpty()) {
return false;
}
username = username.trim();
if (username.length() < 3 || username.length() > 16) {
return false;
}
for (int i = 0; i < username.length(); i++) {
char c = username.charAt(i);
if (!Character.isLetterOrDigit(c)) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(isValid("Steve")); // true
System.out.println(isValid("Player_123")); // false (has underscore)
System.out.println(isValid("ab")); // false (too short)
System.out.println(isValid("")); // false (empty)
}
}
```
### Item Description Builder
```java
public class ItemDescription {
public static String buildDescription(String name, String rarity,
int damage, int durability) {
StringBuilder desc = new StringBuilder();
// Title with rarity color code
desc.append(getRarityColor(rarity));
desc.append(name);
desc.append("\n");
// Stats
desc.append("Damage: ").append(damage).append("\n");
desc.append("Durability: ").append(durability).append("/");
desc.append(durability).append("\n");
desc.append("Rarity: ").append(rarity);
return desc.toString();
}
private static String getRarityColor(String rarity) {
switch (rarity.toLowerCase()) {
case "legendary": return "§6"; // Gold
case "epic": return "§5"; // Purple
case "rare": return "§9"; // Blue
case "common": return "§f"; // White
default: return "§7"; // Gray
}
}
public static void main(String[] args) {
String desc = buildDescription("Excalibur", "Legendary", 50, 1000);
System.out.println(desc);
}
}
```
### Chat Message Filter
```java
public class ChatFilter {
private static String[] bannedWords = {"badword1", "badword2"};
public static String filterMessage(String message) {
String filtered = message.toLowerCase();
for (String word : bannedWords) {
if (filtered.contains(word)) {
String replacement = "*".repeat(word.length());
filtered = filtered.replace(word, replacement);
}
}
return filtered;
}
public static void main(String[] args) {
String message = "This is a badword1 test";
String clean = filterMessage(message);
System.out.println(clean); // "this is a ******** test"
}
}
```
## String Formatting
### Using String.format()
```java
String name = "Alice";
int level = 10;
double health = 85.5;
String formatted = String.format("%s is level %d with %.1f%% health",
name, level, health);
System.out.println(formatted);
// "Alice is level 10 with 85.5% health"
```
Common format codes:
* `%s` - String
* `%d` - Integer
* `%f` - Floating point
* `%.2f` - Float with 2 decimal places
* `%n` - New line (platform independent)
```java
String.format("Name: %s", "Steve"); // "Name: Steve"
String.format("Level: %d", 10); // "Level: 10"
String.format("Health: %.1f", 85.5); // "Health: 85.5"
String.format("Position: (%d, %d, %d)",
10, 64, -5); // "Position: (10, 64, -5)"
```
## Common String Operations
### Check if String is Empty
```java
String text = "";
// Check for empty or null
if (text == null || text.isEmpty()) {
System.out.println("Empty!");
}
// Check for empty, null, or whitespace only
if (text == null || text.trim().isEmpty()) {
System.out.println("Empty or whitespace!");
}
```
### Count Occurrences
```java
public static int countOccurrences(String text, String target) {
int count = 0;
int index = 0;
while ((index = text.indexOf(target, index)) != -1) {
count++;
index += target.length();
}
return count;
}
// Usage
int count = countOccurrences("hello hello world", "hello");
System.out.println(count); // 2
```
### Reverse a String
```java
public static String reverse(String text) {
StringBuilder sb = new StringBuilder(text);
return sb.reverse().toString();
}
// Usage
String reversed = reverse("Hello");
System.out.println(reversed); // "olleH"
```
### Check if Palindrome
```java
public static boolean isPalindrome(String text) {
String cleaned = text.toLowerCase().replaceAll("[^a-z0-9]", "");
String reversed = new StringBuilder(cleaned).reverse().toString();
return cleaned.equals(reversed);
}
// Usage
System.out.println(isPalindrome("racecar")); // true
System.out.println(isPalindrome("hello")); // false
```
## Practice Exercises
1. **Username Checker**: Write a method that checks if a username:
* Is between 3 and 16 characters
* Contains only letters, numbers, and underscores
* Doesn't start with a number
* Returns true if valid, false otherwise
2. **Command Parser**: Parse this command format: `/teleport x y z`
* Extract the coordinates
* Convert them to integers
* Return an array of the three values
3. **Chat Formatter**: Create a method that formats chat messages:
* Input: player name, rank, message
* Output: `"[RANK] PlayerName: message"`
* If no rank, just show `"PlayerName: message"`
4. **Word Counter**: Count how many words are in a sentence (words are separated by spaces)
5. **Title Case**: Convert a string to title case:
* Input: `"hello world"`
* Output: `"Hello World"`
--- 00 - Introduction to Java
Source: https://hytalemodding.dev/en/docs/guides/java-basics/00-introduction
Welcome to your journey into Java Programming! This guide is designed to introduce you into the basics of java.
## What is Java?
Java is a powerful, object-oriented programming language created in 1995. It's one of the most popular programming languages in the world and is used for everything from mobile apps to enterprise software. Most importantly for us, it's the language Hytale uses for its servers & modding API.
## What You'll Learn
This guide is structured to take you step-by-step through:
1. Java Syntax and Basics
2. Control Flow
3. Object-Oriented Programming
We will then focus on only Hytale-specific concepts. (tbd)
## Setting up your Environment
Before we dive into programming, you'll need some essentials, you need to install:
### Java Development Kit (JDK)
Download and install JDK 25 or above from the [official Oracle website](https://www.oracle.com/java/technologies/javase-jdk25-downloads.html) or use an open-source alternative like [AdoptOpenJDK](https://adoptopenjdk.net/).
### Integrated Development Environment (IDE)
An IDE is a software application that provides comprehensive facilities to programmers for software development. Popular choices for Java include:
* [IntelliJ IDEA](https://www.jetbrains.com/idea/)
* [Eclipse](https://www.eclipse.org/)
## How to Use This Guide
1. Read each article in order - Concepts build on each other
2. Type out the examples - Don't copy-paste; typing helps you learn
3. Experiment - Modify examples to see what happens
4. Practice - Each article includes exercises
5. Be patient - Programming is a skill that takes time to develop
## The Modding Mindset
Modding is creative problem-solving. You'll be:
* Adding new blocks, items, and creatures to Hytale
* Changing game mechanics to create unique experiences
* Building systems that interact with the game world
* Sharing your creations with the community
--- 05 - Arrays
Source: https://hytalemodding.dev/en/docs/guides/java-basics/05-arrays
Arrays let you store multiple values of the same type in a single variable. Think of an array as a row of boxes, each holding one item.
## Creating Arrays
### Method 1: Declare and Initialize Separately
```java
int[] numbers; // Declare
numbers = new int[5]; // Create array with 5 spaces
```
### Method 2: All at Once
```java
int[] numbers = new int[5];
```
### Method 3: With Initial Values
```java
int[] numbers = {10, 20, 30, 40, 50};
```
Once you create an array, its size is **fixed**. You can't add or remove elements later!
```java
int[] inventory = new int[9]; // Always has exactly 9 slots
```
## Accessing Array Elements
Arrays use **index** numbers starting from **0**:
```java
String[] players = {"Alice", "Bob", "Charlie"};
System.out.println(players[0]); // "Alice"
System.out.println(players[1]); // "Bob"
System.out.println(players[2]); // "Charlie"
```
### Visual Representation
| Index | 0 | 1 | 2 |
| ----- | ------- | ----- | --------- |
| Value | "Alice" | "Bob" | "Charlie" |
## Modifying Array Values
```java
int[] health = {100, 80, 90};
health[1] = 60; // Change Bob's health to 60
System.out.println(health[0]); // 100
System.out.println(health[1]); // 60
System.out.println(health[2]); // 90
```
## Array Length
Use `.length` to get the size of an array:
```java
int[] scores = {45, 67, 89, 92, 55};
System.out.println(scores.length); // 5
```
Notice there are no parentheses! It's `array.length`, not `array.length()`.
## Looping Through Arrays
### Using For Loop
```java
String[] items = {"Sword", "Shield", "Potion"};
for (int i = 0; i < items.length; i++) {
System.out.println(items[i]);
}
```
### Using Enhanced For Loop (For-Each)
```java
String[] items = {"Sword", "Shield", "Potion"};
for (String item : items) {
System.out.println(item);
}
```
The enhanced for loop is simpler and safer, but you can't modify the array or know the current index.
```java
// When you need the index
for (int i = 0; i < items.length; i++) {
System.out.println(i + ": " + items[i]);
}
// When you just need the values
for (String item : items) {
System.out.println(item);
}
```
## Multidimensional Arrays
Arrays can hold other arrays, creating a grid:
```java
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(grid[0][0]); // 1
System.out.println(grid[1][2]); // 6
System.out.println(grid[2][1]); // 8
```
### Visual Representation
| | 0 | 1 | 2 |
| ----- | - | - | - |
| **0** | 1 | 2 | 3 |
| **1** | 4 | 5 | 6 |
| **2** | 7 | 8 | 9 |
### Looping Through 2D Arrays
Code
Output
```java
int[][] grid = {
{1, 2, 3},
{4, 5, 6}
};
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
System.out.print(grid[row][col] + " ");
}
System.out.println();
}
```
```java
1 2 3
4 5 6
```
## Practical Examples
### Player Inventory System
```java
String[] inventory = new String[9]; // 9 hotbar slots
// Add items
inventory[0] = "Diamond Sword";
inventory[1] = "Shield";
inventory[8] = "Food";
// Display inventory
for (int i = 0; i < inventory.length; i++) {
if (inventory[i] != null) {
System.out.println("Slot " + i + ": " + inventory[i]);
} else {
System.out.println("Slot " + i + ": Empty");
}
}
```
### Find Highest Score
```java
int[] scores = {45, 92, 67, 88, 55, 71};
int highest = scores[0];
for (int i = 1; i < scores.length; i++) {
if (scores[i] > highest) {
highest = scores[i];
}
}
System.out.println("Highest score: " + highest);
```
### Calculate Average
```java
double[] temperatures = {23.5, 25.0, 22.8, 24.3, 26.1};
double sum = 0;
for (double temp : temperatures) {
sum += temp;
}
double average = sum / temperatures.length;
System.out.println("Average temperature: " + average);
```
### Block Grid (2D Array)
```java
String[][] terrain = new String[5][5];
// Fill with grass
for (int x = 0; x < 5; x++) {
for (int z = 0; z < 5; z++) {
terrain[x][z] = "grass";
}
}
// Place some stone
terrain[2][2] = "stone";
terrain[1][3] = "stone";
// Display grid
for (int x = 0; x < terrain.length; x++) {
for (int z = 0; z < terrain[x].length; z++) {
System.out.print(terrain[x][z] + " ");
}
System.out.println();
}
```
## Common Array Operations
### Copy an Array
```java
int[] original = {1, 2, 3, 4, 5};
int[] copy = original.clone();
// Or manually
int[] copy2 = new int[original.length];
for (int i = 0; i < original.length; i++) {
copy2[i] = original[i];
}
```
### Search for a Value
```java
String[] items = {"Sword", "Shield", "Potion", "Bow"};
String target = "Potion";
int foundIndex = -1;
for (int i = 0; i < items.length; i++) {
if (items[i].equals(target)) {
foundIndex = i;
break;
}
}
if (foundIndex != -1) {
System.out.println("Found at index: " + foundIndex);
} else {
System.out.println("Not found");
}
```
### Count Occurrences
```java
String[] blocks = {"stone", "dirt", "stone", "grass", "stone"};
String searchFor = "stone";
int count = 0;
for (String block : blocks) {
if (block.equals(searchFor)) {
count++;
}
}
System.out.println(searchFor + " appears " + count + " times");
```
## Array Limitations
Arrays can't grow or shrink. If you need flexibility, you'll learn about `ArrayList` later!
```java
int[] numbers = new int[5];
// Can't add a 6th element!
// If you need more space, create a new array
int[] bigger = new int[10];
for (int i = 0; i < numbers.length; i++) {
bigger[i] = numbers[i];
}
```
--- 06 - Methods (Functions)
Source: https://hytalemodding.dev/en/docs/guides/java-basics/06-methods-functions
Methods are reusable blocks of code that perform specific tasks. They help you avoid repeating code and make your programs easier to understand.
## What is a Method?
Think of a method as a recipe. You define it once, then use it whenever you need it.
Code
Output
```java
public class Game {
public static void main(String[] args) {
greet(); // Call the method
greet(); // Call it again!
}
// Method definition
public static void greet() {
System.out.println("Welcome to Hytale!");
}
}
```
```text
Welcome to Hytale!
Welcome to Hytale!
```
## Method Structure
```java
public static void methodName() {
// ^ ^ ^ ^ ^
// | | | | |
// access static return name parameters
// modifier type
// Code goes here
}
```
We'll learn what `public` and `static` mean later. For now, just use them.
## Methods with Parameters
Parameters let you pass data into methods:
```java
public static void greetPlayer(String name) {
System.out.println("Hello, " + name + "!");
}
public static void main(String[] args) {
greetPlayer("Alice"); // Hello, Alice!
greetPlayer("Bob"); // Hello, Bob!
}
```
### Multiple Parameters
```java
public static void dealDamage(String target, int damage) {
System.out.println(target + " takes " + damage + " damage!");
}
public static void main(String[] args) {
dealDamage("Zombie", 20); // Zombie takes 20 damage!
dealDamage("Skeleton", 15); // Skeleton takes 15 damage!
}
```
When calling a method, arguments must match the parameter order:
```java
public static void createItem(String name, int quantity, double price) {
// ...
}
// Correct
createItem("Sword", 1, 50.0);
// Wrong - Order matters!
createItem(1, "Sword", 50.0); // Error!
```
## Methods with Return Values
Methods can send data back using `return`:
```java
public static int add(int a, int b) {
int sum = a + b;
return sum;
}
public static void main(String[] args) {
int result = add(5, 3);
System.out.println(result); // 8
}
```
The return type must match what you return:
* `void` - Returns nothing
* `int` - Returns an integer
* `double` - Returns a decimal
* `boolean` - Returns true/false
* `String` - Returns text
```java
public static String getItemName() {
return "Diamond Sword";
}
public static boolean isPlayerAlive(int health) {
return health > 0;
}
public static double calculateDamage(int attack, double multiplier) {
return attack * multiplier;
}
```
Once a method hits `return`, it immediately exits. Code after `return` won't run!
```java
public static int getValue() {
return 10;
System.out.println("This never runs!"); // Unreachable code!
}
```
## Practical Examples
### Health System
Code
Output
```java
public static void displayHealth(String name, int health, int maxHealth) {
double percentage = (health * 100.0) / maxHealth;
System.out.println(name + ": " + health + "/" + maxHealth +
" (" + percentage + "%)");
}
public static void main(String[] args) {
displayHealth("Player", 75, 100);
displayHealth("Boss", 450, 500);
}
```
```text
Player: 75/100 (75.0%)
Boss: 450/500 (90.0%)
```
### Damage Calculator
Code
```java
public static int calculateDamage(int baseAttack, int weaponDamage, boolean isCritical) {
int totalDamage = baseAttack + weaponDamage;
if (isCritical) {
totalDamage *= 2;
}
return totalDamage;
}
public static void main(String[] args) {
int damage1 = calculateDamage(10, 15, false); // 25
int damage2 = calculateDamage(10, 15, true); // 50
System.out.println("Normal hit: " + damage1);
System.out.println("Critical hit: " + damage2);
}
```
### Level Requirements
Code
```java
public static int getXPForLevel(int level) {
return level * 100;
}
public static boolean canLevelUp(int currentXP, int currentLevel) {
int required = getXPForLevel(currentLevel + 1);
return currentXP >= required;
}
public static void main(String[] args) {
int playerXP = 450;
int playerLevel = 4;
if (canLevelUp(playerXP, playerLevel)) {
System.out.println("You can level up!");
} else {
int needed = getXPForLevel(playerLevel + 1) - playerXP;
System.out.println("Need " + needed + " more XP");
}
}
```
### Distance Calculator
Code
```java
public static double calculateDistance(int x1, int y1, int x2, int y2) {
int dx = x2 - x1;
int dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
public static boolean isInRange(int x1, int y1, int x2, int y2, double range) {
double distance = calculateDistance(x1, y1, x2, y2);
return distance <= range;
}
public static void main(String[] args) {
// Check if enemy is in attack range
if (isInRange(0, 0, 5, 5, 10.0)) {
System.out.println("Target in range!");
}
}
```
## Method Overloading
You can have multiple methods with the same name but different parameters:
```java
public static void displayMessage(String message) {
System.out.println(message);
}
public static void displayMessage(String message, int times) {
for (int i = 0; i < times; i++) {
System.out.println(message);
}
}
public static void main(String[] args) {
displayMessage("Hello"); // Calls first version
displayMessage("Hello", 3); // Calls second version
}
```
Methods are considered different if they have:
* Different number of parameters
* Different types of parameters
* Different order of parameters
```java
public static void test(int a) { }
public static void test(int a, int b) { } // Different count
public static void test(double a) { } // Different type
public static void test(String a, int b) { } // Different types
public static void test(int a, String b) { } // Different order
// Wrong - Only return type is different
public static int test(int a) { }
```
## Common Patterns in Hytale Modding
### Item Creation
```java
public static Item createItem(String name, int durability) {
Item item = new Item();
item.setName(name);
item.setDurability(durability);
return item;
}
public static Item createSword() {
return createItem("Sword", 100);
}
public static Item createPickaxe() {
return createItem("Pickaxe", 150);
}
```
### Block Placement Validation
```java
public static boolean canPlaceBlock(int x, int y, int z) {
// Check if position is valid
if (y < 0 || y > 255) {
return false;
}
// Check if block already exists
if (isBlockAt(x, y, z)) {
return false;
}
return true;
}
public static void placeBlock(int x, int y, int z, String type) {
if (canPlaceBlock(x, y, z)) {
// Place the block
System.out.println("Placed " + type + " at (" + x + ", " + y + ", " + z + ")");
} else {
System.out.println("Cannot place block there!");
}
}
```
### Player State Checks
```java
public static boolean isLowHealth(int health, int maxHealth) {
return (health * 100.0 / maxHealth) < 25;
}
public static String getHealthStatus(int health, int maxHealth) {
double percentage = (health * 100.0) / maxHealth;
if (percentage >= 75) {
return "Healthy";
} else if (percentage >= 50) {
return "Injured";
} else if (percentage >= 25) {
return "Critical";
} else {
return "Near Death";
}
}
```
## Best Practices
Use descriptive verb-based names that explain what the method does:
```java
// Good
public static void calculateDamage() { }
public static boolean isPlayerAlive() { }
public static String getItemName() { }
public static void displayInventory() { }
// Bad
public static void dmg() { } // Too short
public static void method1() { } // Not descriptive
public static void stuff() { } // Too vague
```
## Practice Exercises
1. **Temperature Converter**: Write methods to convert:
* Celsius to Fahrenheit: `(C × 9/5) + 32`
* Fahrenheit to Celsius: `(F - 32) × 5/9`
2. **Circle Calculator**: Create methods that calculate:
* Area: `π × radius²`
* Circumference: `2 × π × radius`
* Use `Math.PI` for π
3. **Item Durability**: Write these methods:
* `damageItem(int current, int damage)` - returns new durability
* `isBroken(int durability)` - returns true if durability is less than or equal to 0
* `repairItem(int current, int max)` - returns max durability
4. **Password Validator**: Create a method that checks if a password is valid:
* At least 8 characters long
* Contains at least one number
* Returns true if valid, false otherwise
When writing a password validator, you may need a way to compare array values. For example:
```java
String[] array1 = {"Apple", "Banana"};
String[] array2 = {"Apple", "Banana"};
//Wrong:
array1[0] == array2[0]; // false
/*
Array is a non-primitive data type. Java compares reference (non-primitive) data types by
their location in Java's memory, not by their values.
*/
// To compare the actual values of reference data types, use the .equals() method:
array1[0].equals(array2[0]); // true
// This does not apply to primitive data types.
```
## Common Mistakes
```java
// Wrong - Forgetting return statement
public static int getValue() {
int x = 10;
// Forgot to return!
} !
// Correct
public static int getValue() {
int x = 10;
return x;
}
// Wrong - Wrong return type
public static int getText() {
return "Hello"; // Error! Should return int, not String
}
// Correct
public static String getText() {
return "Hello";
}
// Wrong - Not calling the method
public static void main(String[] args) {
greet; // Error! Missing ()
}
// Correct
public static void main(String[] args) {
greet();
}
```
***
--- 07 - Introduction to Object-Oriented Programming
Source: https://hytalemodding.dev/en/docs/guides/java-basics/07-introduction-oop
Object-Oriented Programming (OOP) is the foundation of Java and Hytale modding. Instead of just having variables and methods floating around, we organize them into **classes** and **objects**.
## What is a Class?
A class is a blueprint for creating objects. Think of it like a recipe or a template.
```java
public class Player {
// Properties (data)
String name;
int health;
int level;
// Behavior (methods)
public void takeDamage(int damage) {
health -= damage;
System.out.println(name + " took " + damage + " damage!");
}
}
```
## What is an Object?
An object is an instance created from a class. If a class is a blueprint, an object is the actual thing built from that blueprint.
```java
public class Main {
public static void main(String[] args) {
// Create objects from the Player class
Player player1 = new Player();
player1.name = "Alice";
player1.health = 100;
player1.level = 5;
Player player2 = new Player();
player2.name = "Bob";
player2.health = 80;
player2.level = 3;
// Use the objects
player1.takeDamage(20); // Alice took 20 damage!
player2.takeDamage(15); // Bob took 15 damage!
}
}
```
**Class** = Blueprint (the idea of a player)
**Object** = Actual thing (Alice, Bob, specific players)
One class can create many objects, just like one recipe can make many cakes!
## Creating a Simple Class
Let's create a `Sword` class for Hytale:
```java
public class Sword {
// Properties
String name;
int damage;
int durability;
// Method to use the sword
public void attack(String target) {
System.out.println("Attacking " + target + " for " + damage + " damage!");
durability -= 1;
if (durability <= 0) {
System.out.println(name + " broke!");
}
}
// Method to display info
public void displayInfo() {
System.out.println("Weapon: " + name);
System.out.println("Damage: " + damage);
System.out.println("Durability: " + durability);
}
}
```
Using the class:
```java
public class Main {
public static void main(String[] args) {
Sword sword = new Sword();
sword.name = "Iron Sword";
sword.damage = 15;
sword.durability = 3;
sword.displayInfo();
sword.attack("Zombie");
sword.attack("Skeleton");
sword.attack("Spider"); // This will break the sword
}
}
```
## Constructors
Instead of setting properties one by one, use a **constructor** to initialize objects:
```java
public class Sword {
String name;
int damage;
int durability;
// Constructor
public Sword(String weaponName, int weaponDamage, int weaponDurability) {
name = weaponName;
damage = weaponDamage;
durability = weaponDurability;
}
public void attack(String target) {
System.out.println("Attacking " + target + " for " + damage + " damage!");
durability--;
}
}
```
Now creating swords is easier:
```java
public class Main {
public static void main(String[] args) {
// Much cleaner!
Sword ironSword = new Sword("Iron Sword", 15, 100);
Sword diamondSword = new Sword("Diamond Sword", 25, 200);
ironSword.attack("Zombie");
diamondSword.attack("Boss");
}
}
```
* Same name as the class
* No return type (not even `void`)
* Called automatically when you use `new`
* Can have multiple constructors (overloading)
```java
public class Item {
String name;
int value;
// Constructor with all parameters
public Item(String name, int value) {
this.name = name;
this.value = value;
}
// Constructor with just name
public Item(String name) {
this.name = name;
this.value = 0; // Default value
}
}
```
## The `this` Keyword
`this` refers to the current object. Use it to clarify when parameter names match property names:
```java
public class Player {
String name;
int health;
public Player(String name, int health) {
this.name = name; // this.name = the property
this.health = health; // name = the parameter
}
}
```
Without `this`, Java gets confused:
```java
public Player(String name, int health) {
name = name; // ❌ Which name? Ambiguous!
health = health; // ❌ Which health? Ambiguous!
}
```
## Practical Examples
### Item Class
```java
public class Item {
String name;
String type;
int quantity;
double weight;
public Item(String name, String type, int quantity, double weight) {
this.name = name;
this.type = type;
this.quantity = quantity;
this.weight = weight;
}
public void use() {
if (quantity > 0) {
quantity--;
System.out.println("Used " + name + ". Remaining: " + quantity);
} else {
System.out.println("No more " + name + " left!");
}
}
public double getTotalWeight() {
return weight * quantity;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Item potion = new Item("Health Potion", "Consumable", 5, 0.5);
potion.use(); // Used Health Potion. Remaining: 4
System.out.println("Total weight: " + potion.getTotalWeight()); // 2.0
}
}
```
### Monster Class
```java
public class Monster {
String name;
int health;
int attack;
boolean isHostile;
public Monster(String name, int health, int attack, boolean isHostile) {
this.name = name;
this.health = health;
this.attack = attack;
this.isHostile = isHostile;
}
public void takeDamage(int damage) {
health -= damage;
System.out.println(name + " took " + damage + " damage!");
if (health <= 0) {
System.out.println(name + " was defeated!");
} else {
System.out.println(name + " has " + health + " health left.");
}
}
public int attackPlayer() {
if (isHostile && health > 0) {
System.out.println(name + " attacks for " + attack + " damage!");
return attack;
}
return 0;
}
public boolean isAlive() {
return health > 0;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Monster zombie = new Monster("Zombie", 50, 10, true);
Monster chicken = new Monster("Chicken", 10, 0, false);
zombie.takeDamage(20); // Zombie took 20 damage!
int damage = zombie.attackPlayer(); // Zombie attacks for 10 damage!
if (zombie.isAlive()) {
System.out.println("Monster is still alive!");
}
}
}
```
### Block Class
```java
public class Block {
String type;
int x, y, z;
boolean isSolid;
int hardness;
public Block(String type, int x, int y, int z, boolean isSolid, int hardness) {
this.type = type;
this.x = x;
this.y = y;
this.z = z;
this.isSolid = isSolid;
this.hardness = hardness;
}
public void breakBlock() {
System.out.println("Breaking " + type + " block at (" + x + ", " + y + ", " + z + ")");
System.out.println("Hardness: " + hardness);
}
public String getPosition() {
return "(" + x + ", " + y + ", " + z + ")";
}
public boolean canWalkThrough() {
return !isSolid;
}
}
```
## Access Modifiers (Preview)
You've seen `public` - it means "anyone can access this". We'll learn more about access control later, but here's a preview:
```java
public class Example {
public String publicVar; // Anyone can access
private String privateVar; // Only this class can access
/* (no modifier) */ String defaultVar; // Package access
}
```
For now, use `public` for everything. We'll learn when to use `private` in the next article.
Classes help you:
* **Organize** related data and methods together
* **Reuse** code easily (create many objects from one class)
* **Model** real-world things (players, items, monsters)
* **Maintain** code (changes in one place affect all objects)
Without classes, managing 100 players would require 100 separate variables for each property. With classes, it's just 100 Player objects!
## Practice Exercises
1. **Create a `Potion` Class**:
* Properties: name, healAmount, uses
* Constructor to set all properties
* Method `drink()` that heals and decreases uses
* Method `isEmpty()` that returns true if uses less than or equal to 0
2. **Create a `Chest` Class**:
* Properties: isLocked, itemCount, capacity
* Constructor
* Method `addItem()` that checks capacity
* Method `unlock()` that sets isLocked to false
* Method `isFull()` that checks if itemCount >= capacity
3. **Create a `Villager` Class**:
* Properties: name, profession, tradeCount
* Constructor
* Method `greet()` that prints a greeting
* Method `trade()` that increases tradeCount
* Method `getInfo()` that displays all properties
4. **Create Multiple Objects**: Using any class you made, create 3 different objects and test all their methods.
--- 12 - Exception Handling
Source: https://hytalemodding.dev/en/docs/guides/java-basics/12-exception-handling
Exceptions are errors that occur while your program is running. Instead of crashing, you can "catch" these errors and handle them gracefully.
## What is an Exception?
An exception is an event that disrupts the normal flow of your program:
```java
String text = null;
System.out.println(text.length()); // NullPointerException - crashes!
```
Without handling, this crashes your mod and breaks the game!
## Try-Catch Blocks
Use `try-catch` to handle exceptions:
```java
try {
// Code that might throw an exception
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
// Code to handle the error
System.out.println("Error: text was null!");
}
System.out.println("Program continues...");
```
1. Code in `try` block runs normally
2. If an exception occurs, execution jumps to `catch` block
3. After `catch` block, program continues normally
4. If no exception occurs, `catch` block is skipped
```java
try {
// Try to do something risky
int result = 10 / 0; // Division by zero!
} catch (ArithmeticException e) {
// Handle the error
System.out.println("Can't divide by zero!");
}
// Program keeps running
```
## Common Exception Types
### NullPointerException
Accessing methods/properties on null objects:
```java
String name = null;
try {
int length = name.length();
} catch (NullPointerException e) {
System.out.println("Name is null!");
}
```
### ArrayIndexOutOfBoundsException
Accessing invalid array index:
```java
int[] numbers = {1, 2, 3};
try {
int value = numbers[10]; // Index 10 doesn't exist!
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid array index!");
}
```
### NumberFormatException
Converting invalid strings to numbers:
```java
try {
int number = Integer.parseInt("abc"); // Not a number!
} catch (NumberFormatException e) {
System.out.println("Invalid number format!");
}
```
### ArithmeticException
Math errors like division by zero:
```java
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Math error!");
}
```
## Multiple Catch Blocks
Handle different exceptions differently:
```java
String input = "abc";
try {
int number = Integer.parseInt(input);
int result = 100 / number;
System.out.println(result);
} catch (NumberFormatException e) {
System.out.println("Invalid number!");
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero!");
}
```
## Catching Multiple Exception Types
Catch multiple exceptions in one block:
```java
try {
// Some risky code
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("Math-related error occurred!");
}
```
## The Finally Block
Code that **always** runs, whether exception occurs or not:
```java
try {
System.out.println("Opening file...");
// Code that might fail
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
} finally {
System.out.println("Closing file...");
// This ALWAYS runs - good for cleanup
}
```
Use `finally` for cleanup tasks that must always happen:
* Closing files
* Releasing resources
* Saving data
* Logging
```java
FileReader file = null;
try {
file = new FileReader("data.txt");
// Read file
} catch (Exception e) {
System.out.println("Error reading file");
} finally {
if (file != null) {
file.close(); // Always close the file!
}
}
```
## Getting Exception Information
The exception object contains useful information:
```java
try {
int result = Integer.parseInt("xyz");
} catch (NumberFormatException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("Type: " + e.getClass().getName());
e.printStackTrace(); // Print full error details
}
```
## Practical Examples
### Safe Player Input
```java
import java.util.Scanner;
public class PlayerInput {
public static int getPlayerChoice(Scanner scanner) {
while (true) {
try {
System.out.print("Enter choice (1-5): ");
String input = scanner.nextLine();
int choice = Integer.parseInt(input);
if (choice < 1 || choice > 5) {
System.out.println("Please enter a number between 1 and 5");
continue;
}
return choice;
} catch (NumberFormatException e) {
System.out.println("Invalid input! Please enter a number.");
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int choice = getPlayerChoice(scanner);
System.out.println("You chose: " + choice);
}
}
```
### Safe Item Durability Update
```java
public class Item {
private String name;
private int durability;
private int maxDurability;
public Item(String name, int maxDurability) {
this.name = name;
this.durability = maxDurability;
this.maxDurability = maxDurability;
}
public void damage(int amount) {
try {
if (amount < 0) {
throw new IllegalArgumentException("Damage cannot be negative!");
}
durability -= amount;
if (durability < 0) {
durability = 0;
}
System.out.println(name + " durability: " + durability + "/" + maxDurability);
if (durability == 0) {
System.out.println(name + " broke!");
}
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
```
### Safe Config Loading
```java
import java.util.HashMap;
public class ConfigLoader {
private HashMap settings;
public ConfigLoader() {
this.settings = new HashMap<>();
loadDefaults();
}
private void loadDefaults() {
settings.put("maxPlayers", "10");
settings.put("difficulty", "normal");
settings.put("pvpEnabled", "true");
}
public int getIntSetting(String key, int defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.out.println("Invalid number for " + key + ", using default");
return defaultValue;
}
}
public boolean getBooleanSetting(String key, boolean defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Boolean.parseBoolean(value);
} catch (Exception e) {
System.out.println("Invalid boolean for " + key + ", using default");
return defaultValue;
}
}
public void setSetting(String key, String value) {
if (key == null || value == null) {
System.out.println("Key and value cannot be null!");
return;
}
settings.put(key, value);
}
}
```
### Safe Array Access
```java
public class Inventory {
private String[] items;
public Inventory(int size) {
this.items = new String[size];
}
public boolean setItem(int slot, String item) {
try {
if (slot < 0 || slot >= items.length) {
throw new ArrayIndexOutOfBoundsException("Invalid slot: " + slot);
}
items[slot] = item;
System.out.println("Placed " + item + " in slot " + slot);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: " + e.getMessage());
return false;
}
}
public String getItem(int slot) {
try {
return items[slot];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid slot: " + slot);
return null;
}
}
}
```
### Command Parser with Error Handling
```java
public class CommandParser {
public static void parseCommand(String command) {
try {
if (command == null || command.trim().isEmpty()) {
throw new IllegalArgumentException("Command cannot be empty!");
}
String[] parts = command.split(" ");
if (parts.length < 2) {
throw new IllegalArgumentException("Invalid command format!");
}
String action = parts[0];
String target = parts[1];
switch (action) {
case "give":
if (parts.length < 4) {
throw new IllegalArgumentException("Usage: give ");
}
String item = parts[2];
int amount = Integer.parseInt(parts[3]);
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive!");
}
System.out.println("Giving " + amount + " " + item + " to " + target);
break;
case "tp":
if (parts.length < 5) {
throw new IllegalArgumentException("Usage: tp ");
}
int x = Integer.parseInt(parts[2]);
int y = Integer.parseInt(parts[3]);
int z = Integer.parseInt(parts[4]);
System.out.println("Teleporting " + target + " to " + x + ", " + y + ", " + z);
break;
default:
throw new IllegalArgumentException("Unknown command: " + action);
}
} catch (NumberFormatException e) {
System.out.println("Error: Invalid number in command!");
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected error: " + e.getMessage());
}
}
public static void main(String[] args) {
parseCommand("give Steve diamond 5");
parseCommand("give Steve sword abc"); // Invalid number
parseCommand("tp Alice 10 64 20");
parseCommand("unknown command"); // Unknown command
}
}
```
## Throwing Exceptions
You can throw your own exceptions:
```java
public class Player {
private int health;
public void setHealth(int health) {
if (health < 0) {
throw new IllegalArgumentException("Health cannot be negative!");
}
if (health > 100) {
throw new IllegalArgumentException("Health cannot exceed 100!");
}
this.health = health;
}
}
```
Throw exceptions when:
* Method receives invalid input
* Operation cannot be completed
* Preconditions are not met
* Something unexpected happens
```java
public void damagePlayer(int damage) {
if (damage < 0) {
throw new IllegalArgumentException("Damage must be positive!");
}
// Apply damage
}
public Item getItem(int slot) {
if (slot < 0 || slot >= inventory.length) {
throw new IndexOutOfBoundsException("Invalid inventory slot!");
}
return inventory[slot];
}
```
## Checked vs Unchecked Exceptions
**Unchecked Exceptions** (RuntimeException):
* Don't need to be caught
* Usually programming errors
* Examples: NullPointerException, ArrayIndexOutOfBoundsException
**Checked Exceptions**:
* Must be caught or declared
* Usually external errors (files, network)
* Examples: IOException, FileNotFoundException
```java
// Unchecked - no need to catch
int result = 10 / 0; // ArithmeticException
// Checked - must catch or declare
try {
FileReader file = new FileReader("data.txt"); // IOException
} catch (IOException e) {
// Handle error
}
```
## Practice Exercises
1. **Safe Division Calculator**: Create a method that divides two numbers. Handle division by zero and invalid input gracefully.
2. **Player Level Setter**: Create a method that sets player level (1-100). Throw an exception if invalid, and handle it in main.
3. **Safe Array Access**: Create a method that safely accesses array elements. Return null if index is invalid instead of crashing.
4. **Config Parser**: Read a config string like "maxPlayers=10" and parse it. Handle invalid formats gracefully.
5. **File Name Validator**: Create a method that checks if a filename is valid (no special characters, not empty). Throw exceptions for invalid names.
--- Player Death Event
Source: https://hytalemodding.dev/en/docs/guides/plugin/player-death-event
# Overview
In this guide, you'll learn how to use the OnDeathSystem to detect when a player dies.
## Code Example
```java
package org.example.plugin.Systems;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
public class TestDeathSystem extends DeathSystems.OnDeathSystem {
@Nonnull
@Override
public Query getQuery() {
return Query.and(Player.getComponentType());
}
@Override
public void onComponentAdded(@Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
Player playerComponent = (Player) store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
Universe.get().sendMessage(Message.raw("Death player: " + playerComponent.getDisplayName()));
Damage deathInfo = component.getDeathInfo();
if (deathInfo != null) {
Universe.get().sendMessage(Message.raw("Death info amount: " + deathInfo.getAmount()));
}
}
}
```
## Steps
### 1. Extend the Death System
Start by creating a new class that extends `DeathSystems.OnDeathSystem`. This allows your system to automatically react whenever an entity dies.
```java
public class TestDeathSystem extends DeathSystems.OnDeathSystem {
```
***
### 2. Define Which Entities This System Applies To
Override the `getQuery()` method to filter for the entities you want. In this case, we only want **players**:
```java
@Nonnull
@Override
public Query getQuery() {
return Query.and(Player.getComponentType());
}
```
`Query.and(...)` allows you to combine multiple filters if needed.
***
### 3. Listen for the Death Event
The `onComponentAdded` method is called automatically whenever a `DeathComponent` is added to an entity matching your query.
```java
@Override
public void onComponentAdded(
@Nonnull Ref ref,
@Nonnull DeathComponent component,
@Nonnull Store store,
@Nonnull CommandBuffer commandBuffer
)
```
***
### 4. Get the Player Component
Use the `Store` to retrieve the `Player` component from the entity that just died:
```java
Player playerComponent = (Player) store.getComponent(ref, Player.getComponentType());
```
This lets you access information such as the player's display name.
***
### 5. Get Death Damage Information
The `DeathComponent` contains information about the damage that killed the player:
```java
Damage deathInfo = component.getDeathInfo();
```
You can then check:
```java
if (deathInfo != null) {
Universe.get().sendMessage(Message.raw("Damage amount: " + deathInfo.getAmount()));
}
```
This provides the **amount of damage** and the **source of the death**.
--- Spawning Entities
Source: https://hytalemodding.dev/en/docs/guides/plugin/spawning-entities
In this guide, you will learn how to spawn entities in Hytale. It is **highly recommended** to familiarize yourself with the Entity Component System (ECS) before proceeding with this guide. You can find the ECS guide [here](../ecs/hytale-ecs-theory).
## Getting the World Object
You need to first get the `World` object before proceeding with spawning entities. You can get this object in a few ways:
### Using the `Player` Object
You can use the `Player` object to get the `World` object by calling the `getWorld()` method on the `Player` instance.
```java
World world = player.getWorld();
```
### Using the `Universe` Class
You can use the `Universe` class to get the `World` object by calling the `getWorld(string UUID)` method on the `Universe` instance.
```java
World world = Universe.get().getWorld("your-world-uuid");
```
Once you have the world object, you need to get a instance of `Store`, you can do so by:
```java
Store store = world.getEntityStore().getStore();
```
## Creating Entities
Once you have the `EntityStore` instance, you can start to create an entity and give it components. Entities are just instances of `Holder` with several components attached to them.
To interact with the `World` instance, some methods require you to run the code inside the world's execution context.
The logic under the hood enqueues each task to be executed in the world's thread. To do this, you can use the `world.execute()` method.
In our case, we will be using the `world.execute()` method to spawn the entity in the world. For convenience, in this guide we will be putting all the code below inside the lambda function, but it's not required.
```java
world.execute(() -> {
// run all the code below in this context
})
```
Let's start by creating a blank holder:
```java
Holder holder = EntityStore.REGISTRY.newHolder();
```
## Getting a Model
You now need a model for your Entity. You can find a list of all entities [here](../../server/entities), we're going to use the Minecart entity for this example:
```java
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset("Minecart");
Model model = Model.createScaledModel(modelAsset, 1.0f);
```
### The Transform Component
The `TransformComponent` is the component that tells Hytale where to place your entity, what it's location is. You can get this in multiple ways.
1. Getting the Transform Component from the player - note that this requires fetching the player's EntityStore reference first.
```java
TransformComponent transform = store.getComponent(playerRef.getReference(), EntityModule.get().getTransformComponentType());
```
2. Generating a new TransformComponent
```java
Vector3d vector3d = new Vector3d(0, 0, 0); // position
Vector3f vector3f = new Vector3f(0, 0, 0); // rotation
TransformComponent transform = new TransformComponent(vector3d, vector3f);
```
## Adding Components to our Entity
Now we need to add all the components we made above to our entity, here's all the components we need to add one by one:
```java
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(vector3d, new Vector3f(0, 0, 0)));
holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(model.toReference()));
holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model));
holder.addComponent(BoundingBox.getComponentType(), new BoundingBox(model.getBoundingBox()));
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(Interactions.getComponentType(), new Interactions()); // you need to add interactions here if you want your entity to be interactable
```
### Ensuring Hytale's Components are present
These act as "configuration" or "default" components that Hytale expects to be present on all entities.
```java
holder.ensureComponent(UUIDComponent.getComponentType());
holder.ensureComponent(Interactable.getComponentType()); // if you want your entity to be interactable
```
## Spawning the Entity
Finally, we can add the entity to the world by calling the `addEntity` method on the `EntityStore` instance:
As a reminder, a part of the logic is to enqueue the entity spawn task inside the world in its thread. Because of this, this part **needs** to be run inside `world.execute()` method.
```java
store.addEntity(holder, AddReason.SPAWN);
```
## Making the entity persist
Hytale automatically saves all entities, but the `NetworkId` component (required for players to be able to see the entity) needs to be manually added every time the entity is loaded.
### Using a system
If you have a component that is unique to your entities, this can be done by creating and registering a custom system as follows:
```java
public class AddNetworkIdToMyEntitySystem extends HolderSystem {
private final ComponentType myEntityComponentType = MyEntityComponent.getComponentType();
private final ComponentType networkIdComponentType = NetworkId.getComponentType();
private final Query query = Query.and(this.myEntityComponentType, Query.not(this.networkIdComponentType));
@Override
public void onEntityAdd(@NotNull Holder holder, @NotNull AddReason reason, @NotNull Store store) {
if (!holder.getArchetype().contains(NetworkId.getComponentType())) {
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
}
}
@Override
public void onEntityRemoved(@NotNull Holder holder, @NotNull RemoveReason reason, @NotNull Store store) {}
@Override
public @Nullable Query getQuery() {
return query;
}
}
```
In your plugin's `setup()` function
```java
this.getEntityStoreRegistry().registerSystem(new AddNetworkIdToMyEntitySystem())
```
### Using PropComponent
Alternatively you can add `PropComponent` to your entity. Hytale adds `PropComponent` to entities spawned with the Entity Tool, and such entities automatically get `NetworkId` and `PrefabCopyableComponent` (which makes the entity work with prefabs).
There is no guarantee, that Hytale will not add some other behavior to this component in the future, which may break your entity, so be careful if you decide to rely on it.
--- Animated Block Textures
Source: https://hytalemodding.dev/en/docs/guides/plugin/animated-block-textures
import { File, Folder, Files } from 'fumadocs-ui/components/files';
So, you want to make your blocks feel more alive through animations, do you?\
Now for the most part this is the exact same as [Creating custom blocks](./creating-block).
## Prerequisites
This is NOT a guide on animating or texturing or even using BlockBench, why? Because I don't know how to do that.\
The only thing we will be doing is taking the fire texture, changing the color, and applying it to our new fire block.
## Setup
To start, you're obviously going to want to grab the `Common/VFX/Fire/Fire.png` file. How you do it is up to you, in the Asset Editor you'll need to switch to the Common tab.\
I'll assume you know how colorize or change the hue of an image, so go ahead and do that now and save it as `Blue_Fire.png`.
The other assets used in this guide are `Common/VFX/Fire/Fire.blockymodel` and `Common/Blocks/Animations/Fire/Fire_Burn.blockyanim` (both renamed to `Blue_Fire`) but since we won't be changing them it's probably fine to leave them. I will reference the `Blue_Fire` versions as a way to point out what should be custom assets.\
I will also duplicate `Server/Item/Item/Deco/Deco_Fire.json` and call it `Deco_Blue_Fire.json`.
## Applying the changes
The `Deco_Blue_Fire` block is actually just a particle emitter, however we will not be using particles, so lets start by removing them in `Block > Particles`.\
Next we want to set the Block Model (called `CustomModel` in JSON) to `VFX/Blue_Fire/Blue_Fire.blockymodel` and Block Model Textures (`CustomModelTexture` in JSON) to
```json
[
{
"Texture": "VFX/Blue_Fire/Blue_Fire.png",
"Weight": 1
}
]
```
Finally, we will apply the .blockyanim to our model, this is done through the Block Model Animation property (`CustomModelAnimation` in JSON) and the value should be `Blocks/Animations/Blue_Fire/Blue_Fire_Burn.blockyanim`.
## Final Code
With that your new blue fire should be done. Here is the minimal JSON for our blue fire.\
There is an additional `Looping` and `RequiresAlphaBlending` property that I have not gone over, however I hope it is self-explanatory.
```json
{
"BlockType": {
"DrawType": "Model",
"CustomModel": "VFX/Blue_Fire/Blue_Fire.blockymodel",
"CustomModelAnimation": "Blocks/Animations/Blue_Fire/Blue_Fire_Burn.blockyanim",
"CustomModelTexture": [
{
"Texture": "VFX/Blue_Fire/Blue_Fire.png",
"Weight": 1
}
],
"Looping": true,
"RequiresAlphaBlending": false
},
"PlayerAnimationsId": "Block"
}
```
## Next steps
Now that you know how to attach an animation to a block, try adding it to a block state.
--- Instance System
Source: https://hytalemodding.dev/en/docs/guides/plugin/instances
# Overview
In this guide, you’ll learn how to use the InstancesPlugin to load an instance and teleport a player to it.
## Spawning an Instance
To create a new instance, you use the `spawnInstance` method. This handles copying the world template and initializing it in the universe.
```java
InstancesPlugin plugin = InstancesPlugin.get();
World currentWorld = /* current world context */;
Transform returnPoint = /* where players should go when they leave */;
// Spawn an instance named "Challenge_Combat_1"
World instanceWorld = InstancesPlugin.get().spawnInstance("Challenge_Combat_1", world, returnPoint).join();
Universe.get().sendMessage(Message.raw("Instance spawned: " + instanceWorld.getName()));
```
Instance templates should be located in your asset pack under `Server/Instances/[Name]`. Each template must contain an `instance.bson` configuration file.
## Teleporting Players to Instances
Once an instance is spawned (or while it is loading), you can move players into it. The `InstancesPlugin` provides utility methods for this that handle state consistency.
### Teleporting to an Active Instance
If the `World` object is already available:
```java
InstancesPlugin.teleportPlayerToInstance(
playerRef,
componentAccessor,
instanceWorld,
null // optional override for return point
);
```
### Teleporting to a Loading Instance
If you have a `CompletableFuture` from `spawnInstance`, you can queue the teleport to happen as soon as the world is ready.
```java
CompletableFuture worldFuture = plugin.spawnInstance("Challenge_Combat_1", currentWorld, returnPoint);
InstancesPlugin.teleportPlayerToLoadingInstance(
playerRef,
componentAccessor,
worldFuture,
null // optional override for return point
);
```
## Exiting Instances
When a player finishes their objective, you can return them to their original world using `exitInstance`. This automatically uses the return point defined during the instance creation.
```java
InstancesPlugin.exitInstance(playerRef, componentAccessor);
```
## Managing Instance Removal
Instances can be configured to remove themselves automatically based on certain conditions, such as being empty for a certain duration.
```java
InstancesPlugin.safeRemoveInstance(instanceWorld);
```
***
## Command Examples
### Spawn Instance Command
```java
public class ExampleSpawnInstanceCommand extends CommandBase {
private final RequiredArg nameArg;
public ExampleSpawnInstanceCommand() {
super("spawninstance", "Spawns a new instance from a template");
this.nameArg = this.withRequiredArg("name", "The name of the new instance", ArgTypes.STRING);
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
UUID playerUUID = ctx.sender().getUuid();
PlayerRef playerRef = Universe.get().getPlayer(playerUUID);
World world = Universe.get().getWorld(playerRef.getWorldUuid());
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
Transform returnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(world, playerRef.getUuid()) : new Transform();
world.execute(() -> {
World instanceWorld = InstancesPlugin.get().spawnInstance(this.nameArg.get(ctx), world, returnPoint).join();
Universe.get().sendMessage(Message.raw("Instance spawned: " + instanceWorld.getName()));
});
}
}
```
***
### Remove Instance Command
```java
public class ExampleRemoveInstanceCommand extends CommandBase {
private final RequiredArg worldArg;
public ExampleRemoveInstanceCommand() {
super("removeinstance", "Safely removes an instance");
this.worldArg = this.withRequiredArg("world", "The world to remove", ArgTypes.STRING);
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
String worldName = ctx.get(this.worldArg);
World targetWorld = Universe.get().getWorld(worldName);
if (targetWorld == null) {
ctx.sendMessage(Message.raw("World not found: " + worldName));
return;
}
targetWorld.execute(() -> {
InstancesPlugin.safeRemoveInstance(targetWorld);
});
}
}
```
***
### Enter Instance Command
```java
public class ExampleEnterInstanceCommand extends CommandBase {
private final RequiredArg nameArg;
public ExampleEnterInstanceCommand() {
super("enterinstance", "Spawns and enters an instance immediately");
this.nameArg = this.withRequiredArg("name", "The name of the instance", ArgTypes.STRING);
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
UUID playerUUID = ctx.sender().getUuid();
PlayerRef playerRef = Universe.get().getPlayer(playerUUID);
World world = Universe.get().getWorld(playerRef.getWorldUuid());
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
Transform returnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(world, playerRef.getUuid()) : new Transform();
world.execute(() -> {
//World instanceWorld = InstancesPlugin.get().spawnInstance(this.nameArg.get(ctx), world, returnPoint).join();
//InstancesPlugin.teleportPlayerToInstance(playerRef.getReference(), playerRef.getReference().getStore(), instanceWorld, (Transform) null);
CompletableFuture worldFuture = InstancesPlugin.get().spawnInstance(this.nameArg.get(ctx), world, returnPoint);
InstancesPlugin.teleportPlayerToLoadingInstance(playerRef.getReference(), playerRef.getReference().getStore(), worldFuture, null);
});
}
}
```
***
### Exit Instance Command
```java
public class ExampleExitInstanceCommand extends CommandBase {
public ExampleExitInstanceCommand() {
super("exitinstance", "Exits the current instance and returns to the previous world");
}
@Override
protected void executeSync(@Nonnull CommandContext ctx) {
Player player = (Player) ctx.sender();
World world = player.getWorld();
world.execute(() -> {
InstancesPlugin.exitInstance(player.getReference(), player.getReference().getStore());
});
}
}
```
--- Browsing the server.jar code
Source: https://hytalemodding.dev/en/docs/guides/plugin/browsing-serverjar
Our [patcher](https://github.com/HytaleModding/patcher) tool allows you to easily prepare an environment for exploring the Hytale Server.
## Why?
When you add a compiled jar as a library, IntelliJ only decompiles class by class and doesn't let you search it. For example, you cannot right click some class and then Find Usages. This script will give you a ready to use Hytale server code as a project where you can explore anything you want.
If anything goes wrong, please ping the author in [this post](https://discord.com/channels/1440173445039132724/1460707397189238785) in the Hytale Modding discord server
## Instructions
If you prefer watching over reading, check out the awesome video walkthrough made by Hyphen45. Note that the written instructions have since been improved, but the process remains the same.
After watching, check out the *Adding HytaleServer as a dependency* section below.
* Clone the repository `git clone https://github.com/HytaleModding/patcher.git`, and enter the directory using `cd patcher`
* Create a Python virtual environment in the `.venv` folder. The command for this varies by platform but it is probably
one of these:
* `python -m venv .venv`
* `python3 -m venv .venv` (Linux)
* `py -3.13 -m venv .venv` (Windows)
In the last one, specifying the version is recommended if you have multiple Pythons installed.
* Activate the virtual environment:
* Windows: If you're using normal command prompt, do `".venv\Scripts\activate"` and if you're using PowerShell, do `.\.venv\Scripts\activate`
* Linux/Mac: `source .venv/bin/activate`
* From now on, you are running python commands from inside the venv, hence you must use `python` instead of `py` or `python3` to invoke python.
* You should now have `(.venv)` prefixed to the current working dir in the terminal.
* Inside the venv, install the dependencies
* `pip install -r requirements.txt`
* Install these dependencies and ensure they are on PATH:
* `git` - download from [here](https://git-scm.com/install/) and make sure the `C:\Program Files\Git\cmd\` folder is on your PATH.
* `java` you need JDK 25 or newer
* `jar` (comes with JDK inside the bin folder)
* `mvn` - download from [here](https://maven.apache.org/download.cgi), extract and add the `bin` folder on your PATH
After modifying your PATH, don't forget to close existing terminals and start a new one. Verify that those tools are installed by running these commands: `git --version`, `mvn --version`, `java --version`, `jar --version`.
* Put your HytaleServer.jar in the same root directory of this repo or specify an environment variable `HYTALESERVER_JAR_PATH` with the path to your HytaleServer.jar
Then run this:
```shell
python run.py setup
```
What will it do:
* copy the HytaleServer.jar into `work/download`
* (on Windows) fix `META-INF/license` name collision
* decompile only the `com.hypixel` package using Vineflower and save the output to `work/decompile`
* set up a Maven project in `hytale-server` with the decompiled code
* all other libraries the server uses are added into pom.xml and you don't have to worry about them.
You can then open the `hytale-server` folder in your favorite IDE and begin exploring the code. For *IntelliJ IDEA*,
you must first set up the SDK. After opening the project (you can open the `pom.xml` file, IDEA will prompt you to open
the entire project) press Ctrl+Alt+Shift+S and under *Project* configure *SDK* and *Language level* to **25**.
This decompiled code is likely broken. But it is somewhat usable for exploration. Try Ctrl+Shift+F and search PacketAdapters. Want to figure out where is PlayerSetupConnectEvent constructed? Just find it somewhere then right click and Find Usages, and at the bottom see New instance creation.
## Adding HytaleServer as a dependency
Go into `hytale-server/src/main/java` folder in your terminal (cmd or powershell). Run this command
```shell
jar -f hytale-server-stripped.jar -c com
```
Then run this **from the same folder**:
```shell
mvn install:install-file -Dfile=hytale-server-stripped.jar -DgroupId="com.hypixel.hytale" -DartifactId=HytaleServer-stripped -Dversion="1.0-SNAPSHOT" -Dpackaging=jar
```
Then, in your plugin project add this dependency and also copy other dependencies from the `hytale-server/pom.xml`.
```xml
com.hypixel.hytaleHytaleServer-stripped1.0-SNAPSHOTprovided
```
Congratulations!
## Reloading Maven projects
You may have to reload the maven project sometimes. Here's how to do that:\
\
It is located in the "m" icon on the right side:\
\
If you don't see it, enable it under View -> Tool Windows -> Maven.\
--- Customizing Camera Controls
Source: https://hytalemodding.dev/en/docs/guides/plugin/customizing-camera-controls
## The Basics
Hytale provides great camera customizability! Heres the essentials to get started.
Camera is controlled by sending a `SetServerCamera` packet with `ServerCameraSettings`:
```java
ServerCameraSettings settings = new ServerCameraSettings();
settings.distance = 10.0f; // Zoom distance from player
settings.isFirstPerson = false; // Third-person mode
settings.positionLerpSpeed = 0.2f; // Smooth camera follow
playerRef.getPacketHandler().writeNoCache(
new SetServerCamera(ClientCameraView.Custom, true, settings)
);
```
This gives you a simple third-person camera. The presets below show more complete configurations.
**Reset to default:**
```java
playerRef.getPacketHandler().writeNoCache(
new SetServerCamera(ClientCameraView.Custom, false, null)
);
```
***
## Camera Presets
### Top-Down (RTS/ARPG Style)
*Source: `com.hypixel.hytale.server.core.command.commands.player.camera.PlayerCameraTopdownCommand`*
```java
ServerCameraSettings settings = new ServerCameraSettings();
settings.positionLerpSpeed = 0.2f;
settings.rotationLerpSpeed = 0.2f;
settings.distance = 20.0f;
settings.displayCursor = true;
settings.isFirstPerson = false;
settings.movementForceRotationType = MovementForceRotationType.Custom;
// Align movement with camera yaw (horizontal rotation only)
settings.movementForceRotation = new Direction(-0.7853981634f, 0.0f, 0.0f); // 45° right
settings.eyeOffset = true;
settings.positionDistanceOffsetType = PositionDistanceOffsetType.DistanceOffset;
settings.rotationType = RotationType.Custom;
settings.rotation = new Direction(0.0f, -1.5707964f, 0.0f); // Look straight down
settings.mouseInputType = MouseInputType.LookAtPlane;
settings.planeNormal = new Vector3f(0.0f, 1.0f, 0.0f); // Ground plane
```
### Side-Scroller (2D Platformer Style)
*Source: `com.hypixel.hytale.server.core.command.commands.player.camera.PlayerCameraSideScrollerCommand`*
```java
ServerCameraSettings settings = new ServerCameraSettings();
settings.positionLerpSpeed = 0.2f;
settings.rotationLerpSpeed = 0.2f;
settings.distance = 15.0f;
settings.displayCursor = true;
settings.isFirstPerson = false;
settings.movementForceRotationType = MovementForceRotationType.Custom;
settings.movementMultiplier = new Vector3f(1.0f, 1.0f, 0.0f); // Lock Z-axis
settings.eyeOffset = true;
settings.positionDistanceOffsetType = PositionDistanceOffsetType.DistanceOffset;
settings.rotationType = RotationType.Custom;
settings.mouseInputType = MouseInputType.LookAtPlane;
settings.planeNormal = new Vector3f(0.0f, 0.0f, 1.0f); // Side plane
```
### Isometric Character Camera (Diablo style)
```java
ServerCameraSettings settings = new ServerCameraSettings();
settings.positionLerpSpeed = 0.2f;
settings.rotationLerpSpeed = 0.2f;
settings.isFirstPerson = false;
settings.distance = 6f;
settings.allowPitchControls = false;
settings.displayCursor = true;
// Force the camera's rotation to be set by the server.
settings.applyLookType = ApplyLookType.Rotation;
// Notify that we provide a custom rotation in "settings.rotation"
settings.rotationType = RotationType.Custom;
// Set the typical isometric rotation to the camera
Direction direction = new Direction(
(float) Math.toRadians(45f), // yaw
(float) Math.toRadians(-35f), // pitch
0f // roll
);
settings.rotation = rotation;
settings.movementForceRotation = rotation;
```
## Common Settings Explained
### Position & Rotation
* **`positionLerpSpeed`** (0.0-1.0): How smoothly the camera follows the player. Lower = smoother but slower response.
* **`rotationLerpSpeed`** (0.0-1.0): How smoothly the camera rotates. Lower = smoother but slower response.
* **`distance`**: Camera distance from player. Higher = zoomed out more.
* **`rotation`**: Camera angle as `Direction(yaw, pitch, roll)` in radians.
* **`rotationType`**: How rotation is calculated. `RotationType.Custom` uses your `rotation` value.
### Movement Alignment
* **`movementForceRotationType`**:
* `MovementForceRotationType.AttachedToHead` = movement follows where player looks
* `MovementForceRotationType.Custom` = use `movementForceRotation` value
* **`movementForceRotation`**: When using `Custom`, this sets the direction for W/S movement. Match yaw with camera but keep pitch at 0.
### Input & Display
* **`displayCursor`**: Show/hide mouse cursor.
* **`mouseInputType`**:
* `MouseInputType.LookAtPlane` = mouse moves cursor on a plane (good for top-down)
* `MouseInputType.LookAtTarget` = mouse rotates camera
* **`planeNormal`**: For `LookAtPlane`, defines which plane the mouse moves on. `(0,1,0)` = ground plane.
### Advanced
* **`positionDistanceOffsetType`**:
* `DistanceOffset` = simple distance offset
* `DistanceOffsetRaycast` = prevents camera clipping through walls
* **`eyeOffset`**: Offset camera from player's eye position.
* **`movementMultiplier`**: Scale movement on each axis. `(1,1,0)` = lock Z-axis for 2D movement.
***
## Imports
```java
import com.hypixel.hytale.protocol.ClientCameraView;
import com.hypixel.hytale.protocol.Direction;
import com.hypixel.hytale.protocol.MouseInputType;
import com.hypixel.hytale.protocol.MovementForceRotationType;
import com.hypixel.hytale.protocol.PositionDistanceOffsetType;
import com.hypixel.hytale.protocol.RotationType;
import com.hypixel.hytale.protocol.ServerCameraSettings;
import com.hypixel.hytale.protocol.Vector3f;
import com.hypixel.hytale.protocol.packets.camera.SetServerCamera;
```
***
## Tips
* **Zoom:** Adjust `distance` (higher = further out)
* **Smoothness:** `positionLerpSpeed` and `rotationLerpSpeed` control how quickly the camera catches up to its target
* **Wall clipping:** Use `PositionDistanceOffsetType.DistanceOffsetRaycast`
* **Lock camera:** Set `isLocked = true` in the packet to prevent player changes
* **2D movement:** Set `movementMultiplier` to zero out an axis
* **Isometric cameras:** Always set `movementForceRotation` to match camera yaw for proper movement alignment
* **Angle calculations:** Use `Math.toRadians(degrees)` or `degrees * Math.PI / 180.0` to convert degrees to radians
***
*From Hytale Server build `2026.01.13-dcad8778f`*
--- Creating Commands
Source: https://hytalemodding.dev/en/docs/guides/plugin/creating-commands
In this guide, you will learn how to create custom commands for your Hytale mod.
You can see information on the following topics:
* Command types
* [AbstractAsyncCommand](#the-abstractasynccommand-class)
* [AbstractPlayerCommand](#the-abstractplayercommand-class)
* [AbstractTargetPlayerCommand](#the-abstracttargetplayercommand-class)
* [AbstractTargetEntityCommand](#the-abstracttargetentitycommand-class)
* [Arguments](#adding-arguments)
* [Argument Validators](#argument-validators)
* [Permissions](#permissions)
* [Command Variants](#command-variants)
* [Subcommands and Command Collections](#sub-commands-and-command-collections)
* [Registering the Command](#registering-the-Command)
* [Video Link](#video-link)
## Command Types
### The `AbstractAsyncCommand` Class
The most basic command is an `AbstractAsyncCommand`. It has very minimal data available to access and interact with. All commands require the user to pass data to the `BaseCommand` constructor using `super`. You will almost always be passing `super(, )` unless you are creating [Command Variants](#command-variants)
`AbstractAsyncCommand` runs on its own background thread. This means its execute is not tied to any world instance and because of this it can not edit any **Stores** or **Refs** without getting the desired world first. To access this data any of the other command types would be preferred and are likely what you will want to use instead.
The main purpose for a command like this would be for something that can happen everywhere and is not tied to a specific world. For example a `serverRules` command to tell the user that's running the command in chat what the server's rules are no matter what world they are in.
```java
// Command that can be run that lists the server rules in chat to the player
public class ServerRulesCommand extends AbstractAsyncCommand {
// Constructor
public ServerRulesCommand() {
// super(, )
super("rules", "Lists the servers rules");
}
// Run the command
// context - info about who ran the command. Server console?, Some player?
@Override
protected CompletableFuture executeAsync(@Nonnull CommandContext context) {
context.sendMessage(Message.raw("The only rule is there are no rules."));
return CompletableFuture.completedFuture(null);
}
}
```
### The `AbstractPlayerCommand` Class
The `AbstractPlayerCommand` is similar to the `AbstractAsyncCommand` but it is tied to a specific Player and World. This allows the command to edit those additional objects that we could not edit before. The limits are that the command is always tied to the player running the command. If you would instead like to target a different player or item use `AbstractTargetPlayerCommand` and `AbstractTargetEntityCommand` respectively.
To create a command, you can extend the `AbstractPlayerCommand` class. You need to provide a constructor that calls the `BaseCommand` constructor with the command name, description, and whether the command requires confirmation (--confirm while running the command).
You can override the `execute` function to implement the command's behavior. It gives you access to the CommandContext which is always a player in this case, the EntityStore, the Reference store, the player reference as well as the World in which the command is being executed.
Unlike `AbstractAsyncCommand` commands using `AbstractPlayerCommand` run on the world thread, which means they can safely access the `Store` and `Ref`s. This also means that long running actions (like IO) can lead to lag or server lockup.
You can use the `Store` along with the `Ref` to access all entity components like the `Player` component, `UUIDComponent` or `TransformComponent`.
For more information about Hytale's Entity Component System visit the [Hytale ECS Theory](../ecs/hytale-ecs-theory) guide.
```java
public class ExampleCommand extends AbstractPlayerCommand {
public ExampleCommand() {
super("test", "Super test command!");
}
@Override
protected void execute(@Nonnull CommandContext commandContext, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world) {
Player player = store.getComponent(ref, Player.getComponentType()); // also a component
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
player.sendMessage(Message.raw("UUIDComponent : " + component.getUuid()));
player.sendMessage(Message.raw("Transform : " + transform.getPosition()));
}
}
```
### The `AbstractTargetPlayerCommand` Class
Like the `AbstractPlayerCommand` but adds a `--player ` argument to allow the user to specify which player they are targeting.
Is automatically threaded so you override the `execute` command instead of `executeAsync`.
[See Example below](#example-command-arg-types)
### The `AbstractTargetEntityCommand` Class
Uses a Ray to see what the player is looking at and tries to run the command on that entity. It is thread safe and will run on whatever world thread that includes the targeted entity. The following shows an example and how the ref provided just becomes a ref to the entity the player was looking at.
```java
...
@Override protected void execute (CommandContext context,
Store store,
Ref ref,
World world) {
EntityStatMap stats = store.getComponent(ref, EntityStatMap.getComponentType());
// Need to verify were looking at something with stats
if (stats != null) {
context.sendMessage(Message.raw("This entity has no stats!"));
return;
}
int healthIdx = DefaultEntityStatTypes.getHealth();
EntityStatValue health = stats.get(healthIdx);
// Need to verify were looking at something with health
if (health != null) {
context.sendMessage(Message.raw("This entity has no health!"));
return;
}
stats.addValue(healthIdx, 100);
}
...
```
***
## Adding Arguments
Arguments are additional fields that the user can input when running a command. The different types of arguments along with small snippets and a large example can be seen below.
### Command Argument Types
* `withRequiredArg` // Must be provided and are parsed left -> right in the command
* `withOptionalArg` // Returns null if not provided. Needs to have the --`` pair when running the command. `EX: --player Joe`
* `withDefaultArg` // Returns a default value if not provided
* `withFlagArg` // boolean switch (--debug)
### Required Arguments
Add a required argument to the command that throws an error if its not present. To do this, you can use the `withRequiredArg` method.
Required args are parsed left to right in the command and don't use a leading flag.
```java
this.healthArg = this.withRequiredArg("health", "Amount to heal player", ArgTypes.FLOAT);
```
### Optional Arguments
Adding a optional argument is the same, only change is the method used to create it. You can use the `withOptionalArg` method.
```java
// Optional args reguare the user the input `--` so the following would require `--message "Good Luck"`
this.messageArg = this.withOptionalArg("message", "Message to print while healing", ArgTypes.STRING);
```
### Default Arguments
Adding a default argument has two additional argument for the default value and default value description. You need to use `withDefaultArg` method.
```java
// args
this.healthArg = this.withDefaultArg("health", "Amount to heal player", ArgTypes.FLOAT, (float)100, "Desc of Default: 100");
```
### Flag Arguments
Similar as above but the flags return is either true if it exists or false if it does not. You need to use `withFlagArg` method.
```java
// No type needed due to being a bool
this.debugArg = this.withFlagArg("debug", "Add debug logs");
```
### ArgTypes
You can find all the available argument types on the [ArgTypes](../../argtypes) page.
### Example Command Arg Types
Shows all Command argument types with different various input types.
**NOTE:** When getting the value of an argument during execute you need to pass the context with `someArg.get(commandContext)`. Where `commandContext` is the first variable in the execute function.
```java
// Example usage: /healplayer --health 50 --message "Feels Good" --debug
public class HealPlayerCommand extends AbstractTargetPlayerCommand {
private final DefaultArg healthArg;
private final OptionalArg messageArg;
private final FlagArg debugArg;
public HealPlayerCommand() {
super("healplayer", "Healing a player for an amount of HP (default: 100)");
// Abstract TargetPlayerCommand passes the player that ran the command by default and implements
// the use of `--player ` to specify someone else
// args
this.healthArg = this.withDefaultArg("health", "Amount to heal player", ArgTypes.FLOAT, (float)100, "Desc of Default: 100");
// Or you could do the following making the health value required instead of with a default.
// You would need to change the declaration to RequiredArg
// this.healthArg = this.withRequiredArg("health", "Amount to heal player", ArgTypes.FLOAT);
// Optional args require the user the input `--` so the following would require `--message "Good Luck"`
this.messageArg = this.withOptionalArg("message", "Message to print while healing", ArgTypes.STRING);
// No type needed due to being a bool
this.debugArg = this.withFlagArg("debug", "Add debug logs");
}
@Override
protected void execute(@NonNullDecl CommandContext commandContext, @NullableDecl Ref ref, @NonNullDecl Ref ref1, @NonNullDecl PlayerRef playerRef, @NonNullDecl World world, @NonNullDecl Store store) {
if (this.debugArg.get(commandContext) == true) { // <-- See commandContext passed to argument here
commandContext.sendMessage(Message.raw("We are debugging"));
}
// Health is stored in a generic stat map to allowing mods/future content to easily add more stats if desired.
EntityStatMap stats = store.getComponent(ref, EntityStatMap.getComponentType());
int healthIdx = DefaultEntityStatTypes.getHealth();
EntityStatValue health = stats.get(healthIdx);
float missing = health.getMax() - health.get();
if (this.debugArg.get(commandContext) == true) { // <-- See commandContext passed to argument here
commandContext.sendMessage(Message.raw("Missing: " + missing + " health"));
commandContext.sendMessage(Message.raw("Adding: " + healthArg.get(commandContext) + " health to "));
commandContext.sendMessage(Message.raw(messageArg.get(commandContext)));
commandContext.sendMessage(Message.raw("Input Value: " + healthArg.get(commandContext) + " Default"));
commandContext.sendMessage(Message.raw("Default Health Value: "+healthArg.getDefaultValue()));
}
stats.addStatValue(healthIdx, healthArg.get(commandContext)); // <-- See commandContext passed to argument here
}
}
```
## Argument Validators
Argument validators are a way to check the command before the command gets executed. They are added with the `addValidator` method on an argument when calling `withXxxArg`. They provide a nice output to the user when an input value is invalid. There are some common validators provided in the `Validators` class such as `greaterThan`, `lessThan`, and `or`, `max`, `range`, etc.
```java
...
// Common check types example
OptionalArg healAmount = withOptionalArg("amount", "Heal Amount", ArgTypes.INTEGER)
.addValidator(Validators.greaterThan(0))
.addValidator(Validators.lessThan(1000));
...
```
### Custom Validators
You can also create custom validators by implementing the `com.hypixel.hytale.codec.validation.Validator` interface. The interfaces forces you to implement the `accept` and `updateSchema` method. The `accept` method takes in the argument value and validates it. If the validation fails, you can use the `fail` method of the `com.hypixel.hytale.codec.validation.ValidationResults` class to return an error message.
The `updateSchema` method is used to update the schema of the command argument. This is used for commands that have dynamic schemas based on the input value. For example, if you have a command that takes in a player name as an argument, you can use the `updateSchema` method to update the schema to include a list of online players for validation.
Here is an example of a custom validator that checks if a string value is equal to a banned word. In this example, the banned word is "badword". If the input value is "badword", the validator will fail and return an error message.
```java
import com.hypixel.hytale.codec.schema.SchemaContext;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.validation.ValidationResults;
import com.hypixel.hytale.codec.validation.Validator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MyCustomValidator implements Validator {
@Nonnull
private final String value;
public MyCustomValidator(@Nonnull String value) {
this.value = value;
}
@Override
public void accept(@Nullable String input, @Nonnull ValidationResults results) {
if (this.value.equalsIgnoreCase(input)) {
results.fail("The given role has been banned.");
}
}
@Override
public void updateSchema(SchemaContext context, @Nonnull Schema target) {
// This method can be used to update the schema of the command argument. This is useful for commands that have dynamic schemas based on the input value. For example, if you have a command that takes in a player name as an argument, you can use the `updateSchema` method to update the schema to include a list of online players for validation.
throw new UnsupportedOperationException("Not implemented yet.");
}
}
```
After the custom validator is created, you can add it to an argument like the following example. In this example, we add the `MyCustomValidator` to a string argument called "role". If the user inputs "badword" as the role, the command will fail and return the error message defined in the `MyCustomValidator`.
```java
...
// Custom validator example
String bannedRole = "badword";
OptionalArg roleArg = withOptionalArg("role", "Role to assign", ArgTypes.STRING)
.addValidator(new MyCustomValidator(bannedRole));
...
```
***
## Permissions
This allows the server to control who has access to specific commands. You add the permissions code into the commands constructor. You need to use the `requirePermission` method. You can list multiple permissions if the command requires the player to have multiple to be able to run. You can also use or blocks to allow running the command if the user has one of the defined permissions.
```java
...
// Commands Constructor
public HealPlayerCommand() {
super("healplayer", "heal a player a given amount of HP");
// Only players with this permission can use this command
requirePermission (HytalePermissions.fromCommand("rules"));
// Add multiple permission if the player needs multiple to access the command
requirePermission (HytalePermissions.fromCommand("usercomands"));
// Use OR block if it requires one from a list of permissions roles
requirePermission (
PermissionRules.or (
HytalePermissions.fromCommand("moderator"),
HytalePermissions.fromCommand("admin")
)
);
}
...
```
You can add players to permissions groups and custom permissions using the `/perm` command.
Use `/perm --help` in game to see usage.
***
## Command Variants
If you want variants of the same command without doing argument parsing you can instead use `addUsageVariant(new OtherCommandClass())` in the original commands constructor.
The only difference is in the `OtherCommandClass()` constructor you don't pass the command name to the super class.
You can also use `addAliases("some_alias", "some_other_alias")` as a way to add shorthand or other ways to write the same command.
An example of both is below.
```java
public class GiveCommand extends AbstractPlayerCommand {
private final RequiredArg itemArg;
public GiveCommand () {
super("give", "Give item to yourself");
this.itemArg = withRequiredArg("item", "Item", ArgTypes.STRING);
addUsageVariant(new GiveOtherCommand());
addAliases("gv", "gMe");
}
// Give commands execute should be here
...
}
// Variant give other command
public static class GiveOtherCommand extends AbstractAsyncCommand {
private final RequiredArg itemArg;
private final RequiredArg playerArg;
public GiveOtherCommand () {
super("Give item to another player"); //NOTE: no command name was specified here!
this.playerArg = withRequiredArg("player", "Target Player", ArgTypes.PLAYER_REF);
this.itemArg = withRequiredArg("item", "Item", ArgTypes.STRING);
}
}
```
***
## Sub Commands and Command Collections
You can create a collection of commands using an `AbstractCommandCollection`. This does not allow the top level command to actually do anything. All commands must be implemented on the same level. But you can implement a `CommandCollection` of `CommandCollections`.
The following shows an example of a layout and rough code outline on how to set it up.
```
/admin
|--user
| |--rules
| |--teleport
|
|--server
|--restart
```
```java
public class RulesCommand extends AbstractPlayerCommand {
// See Example above for making commands
// ...
}
// Teleport command here
// Restart command here
...
// User Collection
public class UserCommandCollection extends AbstractCommandCollection {
public UserCommandCollection () {
super("user", "User commands");
addSubCommand(new RulesCommand());
addSubCommand(new TeleportCommand());
}
}
// Server Collection here
...
public class AdminCommand extends AbstractCommandCollection {
public AdminCommand () {
super("admin", "Admin commands");
addSubCommand(new UserCommandCollection());
addSubCommand(new ServerCommandCollection);
}
}
```
***
## Registering the Command
For any command
```java
public class MyPlugin extends JavaPlugin {
@Override
public void setup() {
// One way to add commands
this.getCommandRegistry().registerCommand(new ExampleCommand());
// Another way to add commands:
CommandRegistry registry = getCommandRegistry();
registry.registerCommand(new ExampleCommand());
}
}
```
***
## Video Link
*Special Thanks to [TroubleDev](https://www.youtube.com/@TroubleDEV) for his excellent video guide on all of these features.*
*Video Link: [TroubleDev - Hytale's Command API Explained](https://www.youtube.com/watch?v=KnmhG9MDMgo)*
--- Permission Management
Source: https://hytalemodding.dev/en/docs/guides/plugin/permission-management
In this guide, you'll learn how to manage permission nodes and groups in your Hytale plugin. Permissions control what actions players can perform on your server, from basic commands to advanced administrative functions.
## What Are Permissions?
Permissions are string identifiers that control access to features and commands. When a player tries to perform an action (like using a command), the server checks if they have the required permission.
**Example permission nodes:**
* `hytale.command.gamemode` - Permission to use the gamemode command
* `hytale.editor.brush.use` - Permission to use brush tools
* `myplugin.teleport.home` - A custom permission for your plugin
Permissions follow a hierarchical naming pattern: `namespace.category.action`
## Key Concepts
Before diving into code, let's understand the core concepts:
| Concept | Description |
| ------------------- | -------------------------------------------------------------------------- |
| **Permission Node** | A string like `myplugin.feature.use` that represents a specific permission |
| **Group** | A named collection of permissions (e.g., "Admin", "VIP", "Default") |
| **Provider** | The backend that stores and retrieves permissions |
| **Wildcard** | A pattern like `*` or `myplugin.*` that matches multiple permissions |
### How Permissions Are Checked
When checking if a player has a permission, Hytale follows this order:
1. **User's direct permissions** - Permissions granted directly to the player
2. **Group permissions** - Permissions from groups the player belongs to
3. **Virtual groups** - Game-mode-based permissions (e.g., Creative mode grants builder tools)
4. **Default value** - Falls back to `false` if no match found
The first definitive match wins. If a player has a permission granted at the user level, group permissions won't override it.
***
## The `PermissionsModule` Class
The `PermissionsModule` class is the central hub for managing permissions. Access it using the singleton pattern:
```java
PermissionsModule perms = PermissionsModule.get();
```
### Available Operations
The module supports these operations:
| Operation | Method |
| ----------------------------- | ----------------------------------------------- |
| Add permissions to user | `addUserPermission(uuid, permissions)` |
| Remove permissions from user | `removeUserPermission(uuid, permissions)` |
| Add permissions to group | `addGroupPermission(groupName, permissions)` |
| Remove permissions from group | `removeGroupPermission(groupName, permissions)` |
| Add user to group | `addUserToGroup(uuid, groupName)` |
| Remove user from group | `removeUserFromGroup(uuid, groupName)` |
| Get user's groups | `getGroupsForUser(uuid)` |
| Check permission | `hasPermission(uuid, permissionNode)` |
The module does **not** support:
* Listing all defined groups
* Deleting a group entirely
Groups are implicitly created when you first add permissions to them.
***
## Checking Permissions
The most common operation is checking if a player has a permission.
### Basic Permission Check
```java
// Returns true if player has the permission, false otherwise
boolean canUse = PermissionsModule.get().hasPermission(playerUUID, "myplugin.feature.use");
if (canUse) {
// Player has permission, allow the action
} else {
// Player lacks permission, deny or show error
}
```
### Permission Check with Default Value
You can specify what to return when no permission is explicitly set:
```java
// Returns true by default if no explicit permission is found
boolean canUse = PermissionsModule.get().hasPermission(playerUUID, "myplugin.feature.use", true);
```
This is useful for features that should be enabled by default but can be revoked.
### Using PermissionHolder Interface
Players and command senders implement `PermissionHolder`, allowing direct checks:
```java
// In a command or event handler where you have access to the player
if (player.hasPermission("myplugin.admin.manage")) {
// Player has admin permission
}
```
***
## Managing User Permissions
### Adding Permissions to a User
Permissions are additive - new permissions are added on top of existing ones, not replaced.
```java
PermissionsModule perms = PermissionsModule.get();
// Add a single permission
perms.addUserPermission(playerUUID, Set.of("myplugin.vip.chat"));
// Add multiple permissions at once
Set newPerms = Set.of(
"myplugin.vip.chat",
"myplugin.vip.fly",
"myplugin.vip.kit"
);
perms.addUserPermission(playerUUID, newPerms);
```
### Removing Permissions from a User
```java
PermissionsModule perms = PermissionsModule.get();
// Remove specific permissions
Set toRemove = Set.of("myplugin.vip.fly");
perms.removeUserPermission(playerUUID, toRemove);
```
***
## Managing Groups
Groups let you assign a collection of permissions to multiple players. This is more manageable than assigning permissions individually.
### Adding Permissions to a Group
If the group doesn't exist, it will be created automatically.
```java
PermissionsModule perms = PermissionsModule.get();
// Create/update the "VIP" group with permissions
Set vipPerms = Set.of(
"myplugin.vip.chat",
"myplugin.vip.fly",
"myplugin.vip.kit"
);
perms.addGroupPermission("VIP", vipPerms);
```
### Removing Permissions from a Group
```java
PermissionsModule perms = PermissionsModule.get();
// Remove specific permissions from the group
Set toRemove = Set.of("myplugin.vip.fly");
perms.removeGroupPermission("VIP", toRemove);
```
### Adding a User to a Group
```java
PermissionsModule perms = PermissionsModule.get();
// Add player to the VIP group - they now have all VIP permissions
perms.addUserToGroup(playerUUID, "VIP");
```
### Removing a User from a Group
```java
PermissionsModule perms = PermissionsModule.get();
// Remove player from VIP group
perms.removeUserFromGroup(playerUUID, "VIP");
```
### Getting a User's Groups
```java
PermissionsModule perms = PermissionsModule.get();
// Get all groups the player belongs to
Set groups = perms.getGroupsForUser(playerUUID);
for (String group : groups) {
System.out.println("Player is in group: " + group);
}
```
***
## Built-in Groups
Hytale includes two default groups:
| Group | Default Permissions | Purpose |
| --------- | --------------------- | --------------------------------- |
| `OP` | `*` (all permissions) | Server operators with full access |
| `Default` | None | Base group for all players |
Players without any explicit group assignment are automatically part of the `Default` group.
***
## Wildcards
Wildcards let you grant or deny multiple permissions with a single pattern.
### Wildcard Patterns
| Pattern | Meaning |
| ------------------- | ------------------------------------------------ |
| `*` | Grants **all** permissions |
| `myplugin.*` | Grants all permissions starting with `myplugin.` |
| `-*` | Denies **all** permissions |
| `-myplugin.admin.*` | Denies all admin permissions in your plugin |
### Example: Admin Group with Wildcards
```java
// Grant all permissions to admins
perms.addGroupPermission("Admin", Set.of("*"));
// Grant all plugin permissions to moderators
perms.addGroupPermission("Moderator", Set.of("myplugin.*"));
// Grant all permissions except admin commands
perms.addGroupPermission("Helper", Set.of(
"*",
"-myplugin.admin.*" // Deny admin permissions
));
```
Negation permissions (starting with `-`) take precedence at each level. Use them carefully to avoid accidentally blocking permissions.
***
## Built-in Permission Nodes
Hytale provides constants for common permissions in the `HytalePermissions` class:
### Command Permissions
```java
// Generate permission for a command
String perm = HytalePermissions.fromCommand("gamemode");
// Result: "hytale.command.gamemode"
// Generate permission for a subcommand
String perm = HytalePermissions.fromCommand("gamemode", "creative");
// Result: "hytale.command.gamemode.creative"
```
### Common Permission Nodes
| Permission | Description |
| ----------------------------- | ---------------------------- |
| `hytale.command.op.add` | Add players to OP group |
| `hytale.command.op.remove` | Remove players from OP group |
| `hytale.editor.brush.use` | Use brush tools |
| `hytale.editor.prefab.use` | Use prefabs |
| `hytale.editor.selection.use` | Use selection tools |
| `hytale.editor.history` | Undo/redo operations |
| `hytale.camera.flycam` | Use fly camera mode |
***
## Listening to Permission Events
You can react to permission changes using Hytale's event system.
### Available Events
| Event | Triggered When |
| ------------------------------------------------ | -------------------------------- |
| `PlayerPermissionChangeEvent.PermissionsAdded` | Permissions added to a user |
| `PlayerPermissionChangeEvent.PermissionsRemoved` | Permissions removed from a user |
| `PlayerGroupEvent.Added` | User added to a group |
| `PlayerGroupEvent.Removed` | User removed from a group |
| `GroupPermissionChangeEvent.Added` | Permissions added to a group |
| `GroupPermissionChangeEvent.Removed` | Permissions removed from a group |
### Example: Reacting to Permission Changes
```java
public class PermissionListener {
public static void onPermissionsAdded(PlayerPermissionChangeEvent.PermissionsAdded event) {
UUID playerUUID = event.getPlayerUuid();
Set added = event.getAddedPermissions();
System.out.println("Permissions added to " + playerUUID + ": " + added);
// Maybe notify the player or update cached data
}
public static void onGroupAdded(PlayerGroupEvent.Added event) {
UUID playerUUID = event.getPlayerUuid();
String groupName = event.getGroupName();
System.out.println("Player " + playerUUID + " joined group: " + groupName);
}
}
```
### Registering the Events
```java
public class MyPlugin extends JavaPlugin {
@Override
public void setup() {
EventRegistry events = this.getEventRegistry();
events.registerGlobal(
PlayerPermissionChangeEvent.PermissionsAdded.class,
PermissionListener::onPermissionsAdded
);
events.registerGlobal(
PlayerGroupEvent.Added.class,
PermissionListener::onGroupAdded
);
}
}
```
***
## Creating a Custom Permission Provider
For advanced use cases, you can create your own permission provider. This is useful for:
* Database-backed permissions (MySQL, MongoDB, etc.)
* Cross-server permission synchronization
* Custom features like permission expiry or contexts
### Implementing PermissionProvider
```java
public class DatabasePermissionProvider implements PermissionProvider {
@Nonnull
@Override
public String getName() {
return "DatabasePermissionProvider";
}
// User permission methods
@Override
public void addUserPermissions(@Nonnull UUID uuid, @Nonnull Set permissions) {
// Save to your database
}
@Override
public void removeUserPermissions(@Nonnull UUID uuid, @Nonnull Set permissions) {
// Remove from your database
}
@Override
public Set getUserPermissions(@Nonnull UUID uuid) {
// Query your database
return Set.of();
}
// Group permission methods
@Override
public void addGroupPermissions(@Nonnull String group, @Nonnull Set permissions) {
// Save to your database
}
@Override
public void removeGroupPermissions(@Nonnull String group, @Nonnull Set permissions) {
// Remove from your database
}
@Override
public Set getGroupPermissions(@Nonnull String group) {
// Query your database
return Set.of();
}
// User-group membership methods
@Override
public void addUserToGroup(@Nonnull UUID uuid, @Nonnull String group) {
// Save to your database
}
@Override
public void removeUserFromGroup(@Nonnull UUID uuid, @Nonnull String group) {
// Remove from your database
}
@Override
public Set getGroupsForUser(@Nonnull UUID uuid) {
// Query your database
return Set.of("Default");
}
}
```
Hytale automatically assigns players to game-mode groups (like `Creative` or `Adventure`) using the first available provider.
If your provider throws an error when the group doesn't exist, the player will be disconnected! Always handle missing groups gracefully.
### Registering Your Provider
```java
public class MyPlugin extends JavaPlugin {
@Override
public void setup() {
// Add your provider alongside the default one
PermissionsModule.get().addProvider(new DatabasePermissionProvider());
}
}
```
The `PermissionsModule` aggregates permissions from all registered providers. This means:
* Permission checks query all providers
* Write operations (add/remove) use the **first** provider
### Replacing the Default Provider
If you want full control, you can remove the default provider:
```java
public class MyPlugin extends JavaPlugin {
@Override
public void setup() {
PermissionsModule perms = PermissionsModule.get();
// Remove the default provider FIRST
perms.removeProvider(perms.getFirstPermissionProvider());
// Then add your provider
perms.addProvider(new DatabasePermissionProvider());
}
}
```
When you remove the default provider, the `/op self` command will stop working since it checks for provider tampering.
***
## Best Practices
### Permission Naming Conventions
Follow the pattern: `namespace.category.action`
```java
// Good - clear hierarchy
"myplugin.admin.ban"
"myplugin.user.teleport.home"
"myplugin.vip.chat.color"
// Bad - unclear structure
"myplugin_ban"
"teleport"
"vipChatColor"
```
### Use Constants for Permissions
Define your permissions as constants to avoid typos:
```java
public final class MyPermissions {
public static final String ADMIN_BAN = "myplugin.admin.ban";
public static final String ADMIN_KICK = "myplugin.admin.kick";
public static final String USER_HOME = "myplugin.user.home";
public static final String VIP_FLY = "myplugin.vip.fly";
}
// Usage
if (player.hasPermission(MyPermissions.ADMIN_BAN)) {
// ...
}
```
### Don't Over-Permission
Only create permissions for actions that genuinely need access control. Not everything needs a permission check.
### Thread Safety
If you create a custom provider, ensure it's thread-safe. Permission checks can occur from multiple threads simultaneously. Use `ReadWriteLock` or `ConcurrentHashMap` for your data structures.
***
## In-Game Commands
Hytale provides built-in commands for managing permissions:
### `/op` Command
| Command | Description |
| --------------------- | ------------------------------------------------------------------ |
| `/op self` | Toggle your own OP status (singleplayer or with `--allow-op` flag) |
| `/op add ` | Add a player to the OP group |
| `/op remove ` | Remove a player from the OP group |
### `/perm` Command
Use `/perm --help` in-game for full usage. Common operations:
| Command | Description |
| ---------------------------------------- | ----------------------------- |
| `/perm user list ` | List user's permissions |
| `/perm user add ` | Add permission to user |
| `/perm user remove ` | Remove permission from user |
| `/perm user group list ` | List user's groups |
| `/perm user group add ` | Add user to group |
| `/perm user group remove ` | Remove user from group |
| `/perm group list ` | List group's permissions |
| `/perm group add ` | Add permission to group |
| `/perm group remove ` | Remove permission from group |
| `/perm test ` | Test if you have a permission |
***
## Summary
You've learned how to:
* **Check permissions** using `PermissionsModule.hasPermission()`
* **Manage user permissions** with `addUserPermission()` and `removeUserPermission()`
* **Work with groups** using `addGroupPermission()`, `addUserToGroup()`, etc.
* **Use wildcards** like `*` and `myplugin.*` for bulk permissions
* **Listen to events** for permission changes
* **Create custom providers** for database-backed permissions
For more advanced permission systems with features like inheritance, expiry, and contexts, we're looking into expanding this guide with advanced permission management techniques in the future.
--- Player Input Guide
Source: https://hytalemodding.dev/en/docs/guides/plugin/player-input-guide
Hytale servers don't receive raw keyboard input. The client interprets keypresses and sends packets describing what action the player wants to perform. You need to intercept these packets or events to create custom behavior.
We can use the `SyncInteractionChains` to check when a player interacts. This has a few InteractionTypes, which are listed at the end of this guide.
We can use the following code to intercept the SyncInteractionChains packet, and run our code whenever a user interacts using the "F" keybind.
```java
public class PacketListener implements PacketWatcher {
@Override
public void accept(PacketHandler packetHandler, Packet packet) {
if (packet.getId() != 290) {
return;
}
SyncInteractionChains interactionChains = (SyncInteractionChains) packet;
SyncInteractionChain[] updates = interactionChains.updates;
for (SyncInteractionChain item : updates) {
PlayerAuthentication playerAuthentication = packetHandler.getAuth();
String uuid = playerAuthentication.getUuid().toString();
InteractionType interactionType = item.interactionType;
if(interactionType == InteractionType.Use){
// code here
}
}
}
}
```
### Interaction Types
You will majorly use the `Primary`, `Secondary` and `Use` interaction types. The `Primary` Interaction Type is triggered when a player left clicks, `Secondary` is triggered when a player right clicks, and `Use` is triggered when a player uses an item.
Here's a list of all Interaction Types:
* Primary (0)
* Secondary (1)
* Ability1 (2)
* Ability2 (3)
* Ability3 (4)
* Use (5)
* Pick (6)
* Pickup (7)
* CollisionEnter (8)
* CollisionLeave (9)
* Collision (10)
* EntityStatEffect (11)
* SwapTo (12)
* SwapFrom (13)
* Death (14)
* Wielding (15)
* ProjectileSpawn (16)
* ProjectileHit (17)
* ProjectileMiss (18)
* ProjectileBounce (19)
* Held (20)
* HeldOffhand (21)
* Equipped (22)
* Dodge (23)
* GameModeSwap (24)
--- Packets: Client-to-Server Reference
Source: https://hytalemodding.dev/en/docs/guides/plugin/client-inputs-reference
Please see [Listening to Packets Guide](https://forum.hytalemodding.dev/d/17-listening-to-packets) for further details
Packet are found in the decompiled server source: `"decompiled/sources/com/hypixel/hytale/protocol/packets"`
***
## Player Packets
Packets sent by the client for player actions, movement, and state updates.
| Packet Name | ID | Fields |
| ----------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SetClientId` | 100 | `clientId` |
| `SetGameMode` | 101 | `gameMode` |
| `SetMovementStates` | 102 | `movementStates` |
| `SetBlockPlacementOverride` | 103 | `enabled` |
| `JoinWorld` | 104 | `clearWorld`, `fadeInOut`, `worldUuid` |
| `ClientReady` | 105 | `readyForChunks`, `readyForGameplay` |
| `LoadHotbar` | 106 | `inventoryRow` |
| `SaveHotbar` | 107 | `inventoryRow` |
| `ClientMovement` | 108 | `movementStates`, `relativePosition`, `absolutePosition`, `bodyOrientation`, `lookOrientation`, `teleportAck`, `wishMovement`, `velocity`, `mountedTo`, `riderMovementStates` |
| `ClientTeleport` | 109 | `teleportId`, `modelTransform`, `resetVelocity` |
| `UpdateMovementSettings` | 110 | `movementSettings` |
| `MouseInteraction` | 111 | `clientTimestamp`, `activeSlot`, `itemInHandId`, `screenPoint`, `mouseButton`, `mouseMotion`, `worldInteraction` |
| `DamageInfo` | 112 | `damageSourcePosition`, `damageAmount`, `damageCause` |
| `ReticleEvent` | 113 | `eventIndex` |
| `DisplayDebug` | 114 | `shape`, `matrix`, `color`, `time`, `fade`, `frustumProjection` |
| `ClearDebugShapes` | 115 | *(none)* |
| `SyncPlayerPreferences` | 116 | `showEntityMarkers`, `armorItemsPreferredPickupLocation`, `weaponAndToolItemsPreferredPickupLocation`, `usableItemsItemsPreferredPickupLocation`, `solidBlockItemsPreferredPickupLocation`, `miscItemsPreferredPickupLocation`, `allowNPCDetection`, `respondToHit` |
| `ClientPlaceBlock` | 117 | `position`, `rotation`, `placedBlockId` |
| `UpdateMemoriesFeatureStatus` | 118 | `isFeatureUnlocked` |
| `RemoveMapMarker` | 119 | `markerId` |
## Inventory Packets
Packets sent by the client for inventory management and item manipulation.
| Packet Name | ID | Fields |
| ----------------------- | --- | ------------------------------------------------------------------------------------------- |
| `UpdatePlayerInventory` | 170 | `storage`, `armor`, `hotbar`, `utility`, `builderMaterial`, `tools`, `backpack`, `sortType` |
| `SetCreativeItem` | 171 | `inventorySectionId`, `slotId`, `item`, `override` |
| `DropCreativeItem` | 172 | `item` |
| `SmartGiveCreativeItem` | 173 | `item`, `moveType` |
| `DropItemStack` | 174 | `inventorySectionId`, `slotId`, `quantity` |
| `MoveItemStack` | 175 | `fromSectionId`, `fromSlotId`, `quantity`, `toSectionId`, `toSlotId` |
| `SmartMoveItemStack` | 176 | `fromSectionId`, `fromSlotId`, `quantity`, `moveType` |
| `SetActiveSlot` | 177 | `inventorySectionId`, `activeSlot` |
| `SwitchHotbarBlockSet` | 178 | `itemId` |
| `InventoryAction` | 179 | `inventorySectionId`, `inventoryActionType`, `actionData` |
## Window Packets
Packets sent by the client for UI window interactions.
| Packet Name | ID | Fields |
| ------------------ | --- | --------------------------------------------------------------- |
| `OpenWindow` | 200 | `id`, `windowType`, `windowData`, `inventory`, `extraResources` |
| `UpdateWindow` | 201 | `id`, `windowData`, `inventory`, `extraResources` |
| `CloseWindow` | 202 | `id` |
| `SendWindowAction` | 203 | `id`, `action` |
| `ClientOpenWindow` | 204 | `type` |
## Other Client Packets
Additional client-to-server packets.
| Packet Name | ID | Fields |
| ------------------------ | --- | -------------------------------------------------- |
| `ClientReferral` | 18 | `hostTo`, `data` |
| `SetUpdateRate` | 29 | `updatesPerSecond` |
| `SetTimeDilation` | 30 | `timeDilation` |
| `SetChunk` | 131 | `x`, `y`, `z`, `localLight`, `globalLight`, `data` |
| `SetChunkHeightmap` | 132 | `x`, `z`, `heightmap` |
| `SetChunkTintmap` | 133 | `x`, `z`, `tintmap` |
| `SetChunkEnvironments` | 134 | `x`, `z`, `environments` |
| `SetFluids` | 136 | `x`, `y`, `z`, `data` |
| `SetPaused` | 158 | `paused` |
| `SetEntitySeed` | 160 | `entitySeed` |
| `SetPage` | 216 | `page`, `canCloseThroughInteraction` |
| `SetServerAccess` | 252 | `access`, `password` |
| `SetMachinimaActorModel` | 261 | `model`, `sceneName`, `actorName` |
| `SetServerCamera` | 280 | `clientCameraView`, `isLocked`, `cameraSettings` |
| `SetFlyCameraMode` | 283 | `entering` |
| `SyncInteractionChains` | 290 | `updates` |
*From Hytale Server build `2026.01.13-dcad8778f`*
--- Playing Sounds
Source: https://hytalemodding.dev/en/docs/guides/plugin/playing-sounds
In this guide, you will learn how to play sounds to a player.
## Sound Indexes
We currently have a lot of Sounds available to be used as a key in the AssetMap, [click here](../../server/sounds) to see the full list of available sounds.
You can use the `getAssetMap()` method from the `SoundEvent` class to get the index of a specified sound:
```java
int index = SoundEvent.getAssetMap().getIndex("SFX_Cactus_Large_Hit");
```
## The Transform Component
The `TransformComponent` is the component that tells Hytale where to play the sound. You can get this in multiple ways.
1. Getting the Transform Component from the `Player` object
```java
TransformComponent transform = store.getComponent(context.senderAsPlayerRef(), EntityModule.get().getTransformComponentType());
```
2. Generating a new TransformComponent (although this is not recommended, as if your player is not close enough they won't hear the sound)
```java
Vector3d vector3d = new Vector3d(0, 0, 0); // position
Vector3f vector3f = new Vector3f(0, 0, 0); // rotation
TransformComponent transform = new TransformComponent(vector3d, vector3f);
```
### PlayerRef
You need a Player Reference, which is a instance of `Ref`. If you have the `Player` object, you can use the `getReference()` method to get a Player Reference
### World
You need a World reference, as the sound needs to be played inside the `execute()` method from the `World` class.
### SoundCategory
There is a `SoundCategory` enum that defines different sound categories, such as `Music`, `Ambient`, `SFX` and `UI`. You can use these categories to specify the type of sound you are playing.
## Playing Sounds
You can use the `SoundUtil` class to play sounds, using the `playSoundEvent3dToPlayer` method.
```java
int index = SoundEvent.getAssetMap().getIndex("SFX_Cactus_Large_Hit");
World world = player.getWorld();
EntityStore store = world.getEntityStore();
Ref playerRef = player.getReference();
world.execute(() -> {
TransformComponent transform = store.getStore().getComponent(playerRef, EntityModule.get().getTransformComponentType());
SoundUtil.playSoundEvent3dToPlayer(playerRef, index, SoundCategory.UI, transform.getPosition(), store.getStore());
});
```
--- Interactable NPCs
Source: https://hytalemodding.dev/en/docs/guides/plugin/Interactable-NPCs
This guide breaks down how interactive NPCs work in Hytale using the vanilla Kweebec Merchant as a reference. By the end, you'll understand the Interactions section of a role JSON and be able to create your own interactive NPC.
> Note: It’s recommended you either start with or also read the [NPC Documentation](https://hytalemodding.dev/en/docs/official-documentation/npc-doc) series, which covers behavior trees, sensors, actions, and states for combat NPCs. This is a thorough foundational piece and helpful in learning the flow of NPCRole JSONs
# Section 1: Properties Overview
The top-level fields define what the NPC is.
| Field | Value | Purpose |
| ----------------------- | ------------------ | --------------------------------------------------------------------- |
| `Type` | `Generic` | Self-contained role, no template inheritance |
| `Appearance` | `Kweebec_Rootling` | The NPC model |
| `MaxHealth` | `50` | Health pool |
| `DefaultPlayerAttitude` | `Neutral` | Doesn't attack players |
| `DefaultNPCAttitude` | `Ignore` | Doesn't react to other NPCs |
| `Invulnerable` | `true` | Can't take damage |
| `StartState` | `Idle` | Initial behavior state |
| `BusyStates` | `["$Interaction"]` | While in this state, other players can't interact (the NPC is "busy") |
| `MotionControllerList` | Walk controller | How the NPC moves physically |
*These are just the properties this merchant uses. For the full list of every available role property, see the NPC Meta Reference* [https://hytalemodding.dev/en/docs/official-documentation/npc-doc](https://hytalemodding.dev/en/docs/official-documentation/npc-doc)
> `BusyStates` **is worth a sidebar:** when the NPC enters `$Interaction`, the engine marks it as busy. Any other player pressing F gets rejected at the packet level (`willInteractWith()` returns false). Rmove `$Interaction` from BusyStates if you want to allow multiple players to open the shop simultaneously (each gets their own shop window as it's per-player).
# Section 2: Instructions (Main Behavior) Flow
The `Instructions` array is the main behavior tree. It runs every tick and controls what the NPC does on its own. This merchant has three states, let’s learn them:
### Idle
```json
{
"Sensor": { "Type": "State", "State": "Idle" },
"Instructions": [
{
"ActionsBlocking": true,
"Sensor": { "Type": "Player", "Range": 8 },
"Actions": [
{ "Type": "PlayAnimation", "Slot": "Status", "Animation": "Wave" },
{ "Type": "State", "State": "Watching" }
]
},
{
"Sensor": { "Type": "Any" },
"BodyMotion": { "Type": "Nothing" }
}
]
}
```
This Idle block is a top level Instruction with two nested instructions within in. Think of this like a nested If statement.
If the State is Idle, proceed into one of these two instructions:
1. If a player is within 8 blocks then play the Wave animation, then transition to Watching. `ActionsBlocking` means the animation finishes before the state changes.
2. If no player within 8 blocks, then stand still.
The `Any` Sensor is always true, **but the order of these matters.** The first nested instruction gets priority, so if a player is within 8 blocks it runs and Any, despite being true, does not.
Both of these are running at every tick. So on server start the NPC will begin in Idle. Once a player enters it’s radius and triggers the Watching State, we move to the next state below.
### Watching
```json
{
"Sensor": { "Type": "State", "State": "Watching" },
"Instructions": [
{
"Continue": true,
"Sensor": { "Type": "Player", "Range": 12 },
"HeadMotion": { "Type": "Watch" }
},
{
"Continue": true,
"Sensor": { "Type": "Any" },
"Actions": [
{
"Type": "Timeout", "Delay": [2, 2],
"Action": { "Type": "PlayAnimation", "Slot": "Status" }
}
]
},
{
"Sensor": { "Type": "Not", "Sensor": { "Type": "Player", "Range": 12 } },
"Actions": [
{ "Type": "PlayAnimation", "Slot": "Status" },
{ "Type": "State", "State": "Idle" }
]
},
{
"Sensor": { "Type": "Any" },
"BodyMotion": { "Type": "Nothing" }
}
]
}
```
Now in the Watching state we have four nested instructions. The first two have `Continue` set, so they run together in the same tick:
1. If a player is within 12 blocks, then track them with head motion. Continue
2. Always (Any) wait 2 seconds and then clear the wave animation. Continue
3. If NO player is within 12 blocks then clear animation, return to Idle. (This only runs if instruction 1 didn't match)
4. Fallback Any (so any other situation) return to Nothing
### $Interaction
```json
{
"Sensor": { "Type": "State", "State": "$Interaction" },
"Instructions": [
{
"Continue": true,
"Sensor": { "Type": "Target", "Range": 10 },
"HeadMotion": { "Type": "Watch" }
},
{
"Sensor": { "Type": "Any" },
"Actions": [
{
"Type": "Timeout", "Delay": [1, 1],
"Action": {
"Type": "Sequence",
"Actions": [
{ "Type": "ReleaseTarget" },
{ "Type": "State", "State": "Watching" }
]
}
}
]
}
]
}
```
The NPC enters it’s 3rd state when a player interacts. Two nested instructions:
1. If the locked-on player is within 10 blocks then watch them with head motion. Continue.
2. Always (Any) wait 1 second then release the target and return to Watching
The 1-second delay prevents the NPC from snapping back to idle the instant the player closes the shop.
# Section 3: InteractionInstruction
The `InteractionInstruction` is a **separate behavior tree** from `Instructions`. It runs once per nearby player (within 10 blocks), every tick, and controls the F-key prompt and what happens when the player presses it.
Let’s go through these 3 Instructions now:
### Instruction 1: Hide the prompt if the player isn't facing the NPC
```json
{
"Sensor": {
"Type": "Not",
"Sensor": { "Type": "CanInteract", "ViewSector": 180 }
},
"Actions": [
{ "Type": "SetInteractable", "Interactable": false }
]
}
```
`CanInteract` checks if the player is within the NPC's 180-degree view sector. The `Not` inverts it: if the player is behind the NPC, hide the F prompt.
### Instruction 2: Show the prompt
```json
{
"Continue": true,
"Sensor": { "Type": "Any" },
"Actions": [
{
"Type": "SetInteractable",
"Interactable": true,
"Hint": "server.interactionHints.trade"
}
]
}
```
If we got past instruction 1 (the player IS facing the NPC), show the F prompt with the "trade" hint text. `Continue` is set so instruction 3 also gets evaluated this tick.
### Instruction 3: Handle the F-key press
```json
{
"Sensor": { "Type": "HasInteracted" },
"Instructions": [
{
"Sensor": {
"Type": "Not",
"Sensor": { "Type": "State", "State": "$Interaction" }
},
"Actions": [
{ "Type": "LockOnInteractionTarget" },
{ "Type": "OpenBarterShop", "Shop": "Kweebec_Merchant" },
{ "Type": "State", "State": "$Interaction" }
]
}
]
}
```
`HasInteracted` fires once when the player presses F (it consumes the input). Inside, there's a check: if the NPC is NOT already in `$Interaction` state, fire three actions:
1. `LockOnInteractionTarget` - store this player as the NPC's target for head tracking
2. `OpenBarterShop` - open the shop UI for this player (per-player, not shared)
3. `State: $Interaction` - transition the NPC into the interaction state
The `Not(State: $Interaction)` check works with `BusyStates` to prevent re-interaction. `BusyStates` blocks new F-key presses at the engine level; this check is a safety net within the behavior tree itself. So if you want multiple users to have access at the same time, avoid both.
### How the Two Trees Connect
The Instructions tree and InteractionInstruction tree work together through the `$Interaction` state:
1. Player approaches, Instructions tree triggers: NPC wave animation, transitions to **Watching**
2. Every tick, InteractionInstruction tree: shows the F prompt if the player is facing the NPC
3. Player presses F, InteractionInstruction tree: `HasInteracted` fires, locks target, opens shop, sets **$Interaction**
4. Instructions tree sees **$Interaction:** watches the player, starts a 1-second timeout
5. Timeout expires, Instructions tree: releases target, returns to **Watching**
# Section 4: Make Your Own
### Changing the Interaction
To create your own interactive NPC, copy the Kweebec Merchant JSON into your asset pack at `Server/NPC/Roles/` and change what happens when the player presses F. You can customize any step, ie add a “Hello!” voice line when they wave too.
The action that fires for F interact is in InteractionInstruction, instruction 3. Replace `OpenBarterShop` with any other interaction action.
**Open a different shop:**
```json
{ "Type": "OpenBarterShop", "Shop": "Your_Shop_Id" }
```
**Use a custom action registered by a plugin:**
```json
{ "Type": "YourCustomAction", "YourField": "your_value" }
```
## Custom Actions
Custom actions are registered with `NPCPlugin.get().registerCoreComponentType()` : the same API that `OpenBarterShop` itself uses. Your plugin can register actions the same way.
`InteractionInstruction` is an extension point. The action that fires when a player presses F doesn't have to be a shop, it can be anything.
`OpenBarterShop` registers using one line:
`NPCPlugin.get().registerCoreComponentType("OpenBarterShop",BuilderActionOpenBarterShop::new);`
So all you have to do is extend that same API.
To show what that looks like in practice, here’s a custom Kweebec Merchant who opens to a dialog box with 2 buttons: Browse Wares and Goodbye. This is a small Java plugin and one .ui layout file, using the exact same InteractionInstruction pipeline from this guide.
*Snippet from the plugin*
Using the same logic, you could easily add an entire dialog tree, quest givers, and more. And that’s just when you stick to having it open a page. When combined with custom plugins, F interact can trigger anything you create.
**Challenge**: Make a Treasure Goblin who drops a loot chest when F interact is triggered. Here are 2 guides that can help you get started
* NPC Tutorial: Melee Combat - [https://hytalemodding.dev/en/docs/official-documentation/npc/11-melee-combat](https://hytalemodding.dev/en/docs/official-documentation/npc/11-melee-combat)
* NPC Meta Reference: [https://hytalemodding.dev/en/docs/official-documentation/npc-doc](https://hytalemodding.dev/en/docs/official-documentation/npc-doc)
> Hint 1: The Rabbit flees from the player and registers F interact without the 180 degree cone
> Hint 2: The Goblin\_Thief flees from the player and drops a chest on death
--- Storing Persistent Data on the Player
Source: https://hytalemodding.dev/en/docs/guides/plugin/store-persistent-data
Here is how to store persistent data using a custom component on a Player.
## Build your Component Class
Creating a custom component class leverages the Component system made with the ECS architecture in mind. For more information on Components, check out the [ECS Guide](../ecs/hytale-ecs-theory#components).
Start by creating your custom component class. This will be extremely similar to how regular components are created, however we'll setup a custom Codec so the server can translate this data to BSON, or the server's internal json encoder.
Each variable you want to store in your component must have its own Codec field defined in the BuilderCodec.
For more information on how to create custom Codecs, check out the [ECS Codec Guide](../ecs/hytale-ecs-theory#codec).
```java
public class YourPlayerData implements Component {
// define some vars!
private int someInteger;
private String someString;
private Map someMap;
public static final BuilderCodec CODEC =
BuilderCodec.builder(YourPlayerData.class, YourPlayerData::new)
.append(new KeyedCodec<>("SomeInteger", Codec.INTEGER),
(data, value) -> data.someInteger = value, // setter
data -> data.someInteger) // getter
.addValidator(Validators.nonNull())
.add()
.append(new KeyedCodec<>("SomeString", Codec.STRING),
(data, value) -> data.someString = value, // setter
data -> data.someString) // getter
.add()
.append(new KeyedCodec<>("SomeMap",
new MapCodec<>(Codec.STRING, HashMap::new, false)),
(data, value) -> data.someMap = value, // setter
data -> data.someMap) // getter
.add()
.build();
// Getters and Setters are for the purpose of this example omitted.
// constructor
public YourPlayerData() {
this.someInteger = 0;
this.someString = "";
this.someMap = new HashMap<>();
}
// copy constructor for cloning
public YourPlayerData(YourPlayerData clone) {
this.someInteger = clone.someInteger;
this.someString = clone.someString;
this.someMap = clone.someMap;
}
@Nonnull
@Override
public Component clone() {
return new YourPlayerData(this);
}
}
```
## Register your Component
Inside your main class's `setup()` method, register your new Component.
```java
public class YourPlugin extends JavaPlugin {
private ComponentType yourPlayerDataComponent;
public static YourPlugin instance;
public YourPlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
}
@Override
protected void setup(){
this.yourPlayerDataComponent = this.getEntityStoreRegistry().registerComponent(
YourPlayerData.class,
"YourPlayerDataComponent",
YourPlayerData.CODEC
);
}
public ComponentType getYourPlayerDataComponent() {
return this.yourPlayerDataComponent;
}
}
```
## Using your Component
To use your newly created component, you can add the Component to a player using the `addComponent` method, although this only adds it temporarily, meaning when the player/entity leaves the world, the component is removed. You can use `putComponent` to ensure the component persists between sessions.
```java
// You will need the Ref and Store to apply components
private void someEntryMethod(@Nonnull Ref ref, @Nonnull Store store)
{
// Any entity will work, for now we will use the player
Player player = (Player) store.getComponent(ref, Player.getComponentType());
// since we're using putComponent, it could be useful to check if it already exists on the player in case we want to update it
if(store.getComponent(ref, YourPlugin.instance.getYourPlayerDataComponent()) != null)
{
// here we implement logic that updates the component
var comp = store.getComponent(ref, YourPlugin.instance.getYourPlayerDataComponent())
// declare public fields or methods in the component class
comp.SomeString = "An Updated Field";
} else {
// Here we put the component
var myCustomComp = new CustomComponent();
// declare public fields or methods in the component class
myCustomComp.SomeString = "Edited String";
// putComponent allows you to insert declared objects
store.putComponent(ref, YourPlugin.instance.getYourPlayerDataComponent(), myCustomComp);
}
}
```
## Using your Data
Your component will be stored within the `Store` when added to the player. In the provided example,
we'll fetch this data off the player using the `ensureAndGetComponent()` method, which will add the component to
the player if it does not exist with the default values.
```java
public class IncompleteCustomCommand extends AbstractPlayerCommand {
public IncompleteCustomCommand() {
super("nope", "don't use this command");
}
@Override
protected void execute(
@NonNullDecl CommandContext commandContext,
@NonNullDecl Store store,
@NonNullDecl Ref ref,
@NonNullDecl PlayerRef playerRef,
@NonNullDecl World world
) {
YourPlayerData customData = store.ensureAndGetComponent(ref, YourPlugin.instance().getYourPlayerDataComponent());
// ensureAndGetComponent adds the component to the ref, with the default values defined inside your component.
// use your data
}
}
```
And that's it! Your data will now be saved and loaded automatically with the player.
Using custom components to store persistent data on players is a powerful way to maintain state across sessions.
By following the steps outlined above, you can easily create, register, and utilize your own data structures within the Hytale ECS framework.
This approach ensures that your data is seamlessly integrated with the game's existing systems.
For more information about Hytale's Entity Component System visit the [Hytale ECS Theory](https://hytalemodding.dev/en/docs/guides/ecs/hytale-ecs-theory) guide.
--- Working with the Item Registry
Source: https://hytalemodding.dev/en/docs/guides/plugin/item-registry
In this guide, you'll learn how to work with Hytale's Item Registry system. This is useful when you need to programmatically list all available items, validate item IDs, or retrieve item properties at runtime.
## Prerequisites
Before diving in, you should be familiar with:
* [Setting up your development environment](./setting-up-env)
* [Creating commands](./creating-commands) (optional, for testing)
## Required Imports
To work with the Item Registry, you'll need the following imports:
```java
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
```
## Getting the Item Registry
The Item Registry is accessed through the `Item.getAssetMap()` method, which returns a `DefaultAssetMap` containing all registered items in the game:
```java
DefaultAssetMap itemMap = Item.getAssetMap();
```
## Listing All Items
To iterate through all items in the registry, you can get the underlying map and loop through its entries:
```java
DefaultAssetMap itemMap = Item.getAssetMap();
var map = itemMap.getAssetMap();
for (var entry : map.entrySet()) {
String itemId = String.valueOf(entry.getKey());
Item item = entry.getValue();
// Do something with the item
LOGGER.atInfo().log("Item ID: " + itemId);
}
```
You can also get the total count of registered items:
```java
int totalItems = map.size();
```
## Checking if an Item Exists
To check if a specific item exists in the registry, use `getAsset()` and verify the result:
```java
public boolean itemExists(String itemId) {
var assetMap = Item.getAssetMap();
if (assetMap != null) {
Item item = assetMap.getAsset(itemId);
return item != null && item != Item.UNKNOWN;
}
return false;
}
```
The `Item.UNKNOWN` constant represents an invalid or unrecognized item. Always check for both `null` and `Item.UNKNOWN` when validating items.
## Accessing Item Properties
Once you have an `Item` instance, you can access various properties:
```java
var assetMap = Item.getAssetMap();
Item item = assetMap.getAsset("Soil_Grass");
if (item != null && item != Item.UNKNOWN) {
String id = item.getId(); // Soil_Grass
int maxStack = item.getMaxStack(); // 100
boolean isBlock = item.hasBlockType(); // true
boolean isConsumable = item.isConsumable(); // false
}
```
## Related Guides
* [Inventory Management](./inventory-management) - Learn how to add and remove items from player inventories
* [Create Custom Item and Interaction](./item-interaction) - Learn how to create your own custom items
--- Spawning NPCs
Source: https://hytalemodding.dev/en/docs/guides/plugin/spawning-npcs
In this guide, you will learn how to spawn Non-Player Characters (NPCs) using the `NPCPlugin` helper. This is a more convenient alternative to manually spawning entities with `Holder` as described in the [Spawning Entities](./spawning-entities) guide.
It is **highly recommended** to familiarize yourself with the Entity Component System (ECS) before proceeding. You can find the ECS guide [here](./ecs).
## Using NPCPlugin
The `NPCPlugin` simplifies the process of creating an entity, attaching a model, and handling initial setup. It abstracts away much of the boilerplate required when constructing a `Holder` manually.
### Spawning the NPC
To spawn an NPC, you use the `NPCPlugin.get().spawnNPC(...)` method. This handles the creation of the entity, assigning the model, and positioning it in the world.
```java
Pair, INonPlayerCharacter> result = NPCPlugin.get().spawnNPC(store, "Kweebec_Sapling", null, position, rotation);
```
**Parameters:**
* `store`: The entity store where the NPC will exist.
* `"Kweebec_Sapling"`: The key/name of the entity model/type.
* `null`: Optional configuration (passed as null here).
* `position`: Where to spawn the NPC.
* `rotation`: How the NPC is oriented.
### Handling the Result
The method returns a `Pair`. You should always check if the result is `not null` to ensure the spawn was successful.
* `result.first()`: Returns the `Ref`, which is the reference to the entity in the ECS. You need this to add components or modify the entity later.
* `result.second()`: Returns the `INonPlayerCharacter` interface, providing NPC-specific methods.
```java
if (result != null) {
Ref npcRef = result.first();
INonPlayerCharacter npc = result.second();
// Proceed with customization...
setupNPCInventory(npcRef, store);
}
```
## Customizing the NPC
Once spawned, you can customize the NPC, for example, by modifying its inventory.
### Accessing the Inventory
First, retrieve the `NPCEntity` component to access inventory settings.
```java
NPCEntity npcComponent = store.getComponent(npcRef, Objects.requireNonNull(NPCEntity.getComponentType()));
// Initialize inventory size (e.g., 3 rows, 9 columns, 0 offset)
npcComponent.setInventorySize(3, 9, 0);
```
### Adding Items
You can then access the `Inventory` object to add items or equip armor.
```java
Inventory inventory = npcComponent.getInventory();
// Add a Thorium Mace to the first slot of the hotbar
inventory.getHotbar().addItemStackToSlot((short) 0, new ItemStack("Weapon_Mace_Thorium", 1));
// Equip a Thorium Helmet using the InventoryHelper
InventoryHelper.useArmor(inventory.getArmor(), "Armor_Thorium_Head");
```
> **Note:** `InventoryHelper` is a utility class provided directly by the Hytale API.
## Full Example Command
Here is the complete code for a command that spawns a "Kweebec Sapling" NPC at the player's location and sets up its inventory.
```java
import com.example.npc.NPCPlugin; // Adjust imports as necessary
import hytale.server.plugin.npc.INonPlayerCharacter;
import hytale.server.plugin.npc.NPCEntity;
// ... other imports
public class SpawnNpcCommand extends AbstractPlayerCommand {
public SpawnNpcCommand() {
// Register the command /npc which will trigger the "spawn npc" action
super("npc", "spawn npc");
}
@Override
protected void execute(@NonNullDecl CommandContext commandContext,
@NonNullDecl Store store,
@NonNullDecl Ref ref,
@NonNullDecl PlayerRef playerRef,
@NonNullDecl World world) {
// Get the player's current position to spawn the NPC at the same location
Vector3d position = playerRef.getTransform().getPosition();
// Define the initial rotation (facing direction) for the NPC
Vector3f rotation = new Vector3f(0, 0, 0);
// Use the NPCPlugin helper to spawn the NPC.
Pair, INonPlayerCharacter> result = NPCPlugin.get().spawnNPC(store, "Kweebec_Sapling", null,
position, rotation);
if (result != null) {
// Successfully spawned
Ref npcRef = result.first();
// Retrieve the NPC interface if needed for further interaction
INonPlayerCharacter npc = result.second();
// Set up the NPC's inventory and equipment
setupNPCInventory(npcRef, store);
}
}
/**
* Configures the inventory for the spawned NPC.
*/
public void setupNPCInventory(Ref npcRef, Store store) {
// Retrieve the NPCEntity component to access inventory settings
NPCEntity npcComponent = store.getComponent(npcRef, Objects.requireNonNull(NPCEntity.getComponentType()));
if (npcComponent == null)
return;
// Initialize inventory size (e.g., 3 rows, 9 columns, 0 offset)
npcComponent.setInventorySize(3, 9, 0);
// Add items to the initialized inventory
addItemsToNPCInventory(npcComponent.getInventory());
}
/**
* Adds specific items and armor to the NPC's inventory.
*/
public void addItemsToNPCInventory(Inventory inventory) {
// Add a Thorium Mace to the first slot of the hotbar
inventory.getHotbar().addItemStackToSlot((short) 0, new ItemStack("Weapon_Mace_Thorium", 1));
// Equip a Thorium Helmet using the InventoryHelper
InventoryHelper.useArmor(inventory.getArmor(), "Armor_Thorium_Head");
// Set the active hotbar slot to the weapon
inventory.setActiveHotbarSlot((byte) 0);
}
}
```
## Registering the Command
Finally, do not forget to register the command in your main plugin class:
```java
public class MyHytaleMod extends JavaPlugin {
@Override
protected void setup() {
this.getCommandRegistry().registerCommand(new SpawnNpcCommand());
}
}
```
--- Inventory Management
Source: https://hytalemodding.dev/en/docs/guides/plugin/inventory-management
In this guide, you'll learn how to manage player inventories in your Hytale mod.
## Accessing the Player Inventory
To access the Inventory of a `Player`, you can use the `getInventory()` method which returns the `Inventory` object.
```java
Inventory inventory = player.getInventory();
```
## Opening Inventories
You can open different types of inventories, these inventories are known as "Pages". You can also add custom pages, this will be covered in a guide later on.
You can use the `Page` enum to reference existing inventories, currently the following pages are available:
* `Page.None`
* `Page.Bench`
* `Page.Inventory`
* `Page.ToolsSettings`
* `Page.Map`
* `Page.MachinimaEditor`
* `Page.ContentCreation`
* `Page.Custom`
```java
PageManager pageManager = player.getPageManager();
Store store = player.getWorld().getEntityStore().getStore();
pageManager.setPage(player.getReference(), store, Page.Inventory);
```
## `ItemStack` Class
You can create and manipulate items in a player's inventory using the `ItemStack` class. This class represents a stack of items, and provides methods for managing the quantity and type of items in the stack.
### Creating an ItemStack
To create an `ItemStack`, you need to specify the material type and the quantity of items in the stack.
```java
ItemStack item = new ItemStack("Stone");
ItemStack withQuantity = new ItemStack("Stone", 64);
```
#### Adding custom metadata
You can also add custom metadata to a `ItemStack` by passing a `BsonDocument` when creating it.
```java
BsonDocument metadata = new BsonDocument();
metadata.append("customData", new BsonString("value"));
ItemStack item = new ItemStack("Stone", 64, metadata);
```
### Setting a durability
```java
ItemStack stackWithDurability = new ItemStack(
"DiamondSword", // itemId
1, // quantity
100.0, // durability
100.0, // maxDurability
metadata // metadata (optional)
);
```
## Getting the `ItemContainer`
The `Inventory` class provides methods to get multiple `ItemContainer` objects, like:
* `.getStorage()`
* `.getArmor()`
* `.getBackpack()`
* `.getHotbar()`
* `.getUtility()`
There are also combined methods like:
* `.getCombinedEverything()`
* `.getCombinedArmorHotbarStorage()`
* `.getCombinedBackpackStorageHotbar()`
* `.getCombinedHotbarFirst()`
* `.getCombinedStorageFirst()`
* `.getCombinedArmorHotbarUtilityStorage()`
* `.getCombinedHotbarUtilityConsumableStorage()`
## Adding `ItemStack` objects to the Inventory
To add an `ItemStack` to a player's inventory, you can use the `addItemStack()` method of the `ItemContainer` class.
```java
Inventory inventory = player.getInventory();
ItemContainer storageContainer = inventory.getStorage();
storageContainer.addItemStack(item);
```
Or you can specify a certain slot to add it to:
```java
storageContainer.addItemStackToSlot((short) 4, stack)
```
## Removing `ItemStack` objects from the Inventory
To remove an `ItemStack` from a player's inventory, you can use the `removeItemStack()` method of the `Inventory` class.
```java
Inventory inventory = player.getInventory();
ItemContainer storageContainer = inventory.getStorage();
storageContainer.removeItemStack(item);
```
Or you can specify a certain slot to remove it from:
```java
storageContainer.removeItemStackFromSlot((short) 4);
```
## Custom Inventories (Item Containers)
#### Adding an inventory to an item
You can add a custom inventory by adding the following to the items json
```json
"Container": {
"Capacity": 9
},
```
And adding the following interaction allows opening the inventory UI Page by using a pre defined UI page. It opens the UI like how it would for a backpackpack.
This allows the player to see their inventory along with the items.
```json
"Interactions": {
"Use": {
"Interactions": [
{
"Type": "OpenItemStackContainer"
}
]
}
},
```
#### Plugin Interaction of Container Items
The items stored in an inventory are saved in the item BSON. Because of this they can not be access data through ECS when wanting to edit items.
You will instead need to load the data from the BSON to interact with it and save it back to the BSON.
```java
// Get held item that we know has a container field & its position in invenotory (Assume item its held for example)
ItemStack heldItemStack = player.getInventory().getActiveHotbarItem();
final short ACTIVE_HOTBAR_SLOT = player.getInventory().getActiveHotbarSlot();
// Get the container BSON of the item
BsonDocument containerBSON = heldItemStack.getFromMetadataOrNull(ItemStackItemContainer.CONTAINER_CODEC);
// Get the array of items in the container BSON
ItemStack[] containerItems = ItemStackItemContainer.ITEMS_CODEC.getOrNull(containerBSON, new ExtraInfo());
```
You can then modify the list of items however you want but this will not modify the item directly as item stacks on the player are immutable.
To update the container the player is holding you will need to edit the item locally, then replace the held item with the newly made local copy.
The following shows how to do that.
```java
// For this example we are just removing the first item in the locally saved container (Continued from above)
containerItems[0] = null;
// Update the container fields in the BSON doc to have the removed item
ItemStackItemContainer.ITEMS_CODEC.put(containerBSON, containerItems, new ExtraInfo());
// Create a new version of the item
ItemStack updatedItem = heldItemStack.withMetadata(ItemStackItemContainer.CONTAINER_CODEC, containerBSON);
// Replace the equiped item in the players inventory with the new item as its not editable!
// - remove original item
player.getInventory().getHotbar().removeItemStackFromSlot(ACTIVE_HOTBAR_SLOT);
// - add new item
player.getInventory().getHotbar().setItemStackForSlot(ACTIVE_HOTBAR_SLOT, updatedItem);
```
#### Getting Components from Container Items
You can use a similar method to get any component from an item in a container
```java
// Get the component data
CustomComponent customComponent = itemStack.getFromMetadataOrDefault(CustomComponent.CUSTOM_COMPONENT_ID, CustomComponent.CODEC);
// Modify component data here
customComponent.setSomeValue();
// Set the component data by creating new item with updated info
ItemStack newItem = itemStack.withMetadata(
CustomComponent.CUSTOM_COMPONENT_ID,
CustomComponent.CODEC,
customComponent
);
// Add updated item to the player
player.getInventory().getHotbar().setItemStackForSlot(0, newItem);
```
--- Sending notifications
Source: https://hytalemodding.dev/en/docs/guides/plugin/send-notifications
## Overview
In this guide, you will learn how to send notifications to players, similar to
the notification received when picking up an item from the ground.
## Structure of a Notification
A notification consists of three main components:
1. **Primary Message**: The main `Message` displayed in the notification.
2. **Secondary Message**: Additional `Message`, displayed below the primary message.
3. **Icon**: An item icon that visually represents the notification, shown on
the left side.
## Sending Notifications
You can use the `NotificationUtil` class to send notifications to players.
You will need access to the `PacketHandler` of the player, which can be obtained
from the `PlayerRef` using the `getPacketHandler()` method (in this example,
we get the `PlayerRef` from the `Universe` using the player's UUID).
```java
public static void onPlayerReady(PlayerReadyEvent event) {
var player = event.getPlayer();
var playerRef = Universe.get().getPlayer(player.getUuid());
var packetHandler = playerRef.getPacketHandler();
var primaryMessage = Message.raw("THIS WORKS!!!").color("#00FF00");
var secondaryMessage = Message.raw("This is the secondary message").color("#228B22");
var icon = new ItemStack("Weapon_Sword_Mithril", 1).toPacket();
NotificationUtil.sendNotification(
packetHandler,
primaryMessage,
secondaryMessage,
(ItemWithAllMetadata) icon);
}
```
--- Add a Custom Mod Icon
Source: https://hytalemodding.dev/en/docs/guides/plugin/add-mod-icon
import { File, Folder, Files } from 'fumadocs-ui/components/files';
# Overview
This guide explains how to add a mod icon to your mod inside the World Mod Settings tab, as shown in the image below.
## Steps
1. Prepare your mod icon. If you don’t already have one, you’ll need to create it.
2. Resize your mod icon to 256x256 and save the new PNG file as `icon-256.png`.
3. Move the `icon-256.png` file to your mod's resources folder.
| File | Location | File Resolution |
| ------------ | ---------- | --------------- |
| icon-256.png | resources/ | 256x256 |
--- Create Custom Item and Interaction
Source: https://hytalemodding.dev/en/docs/guides/plugin/item-interaction
import { File, Folder, Files } from 'fumadocs-ui/components/files';
Creating a custom item follows the same setup than the [Creating custom blocks](./creating-block).
## Setup
1. Enable asset packs in `manifest.json` by setting `IncludesAssetPack` to `true`
2. Create the following folder structure: