Loading...
Loading...
Create custom entities and NPCs for Hytale with AI behaviors, components, spawning, and animations. Use when asked to "create a custom entity", "add an NPC", "make a mob", "design AI behavior", or "configure entity spawning".
npx skill4agent add mnkyarts/hytale-skills hytale-custom-entitiesEntity
├── LivingEntity (has health, inventory, stats)
│ ├── Player
│ └── NPCEntity (has AI role, pathfinding)
├── BlockEntity (block-attached entities)
├── ProjectileEntity
└── ItemEntity (dropped items)my-plugin/
└── assets/
└── Server/
└── Content/
├── Entities/
│ └── my_creature.entity
├── Roles/
│ └── my_creature_role.role
└── Spawns/
└── my_creature_spawn.spawnmy_creature.entity{
"DisplayName": {
"en-US": "Custom Creature"
},
"Model": "MyPlugin/Models/custom_creature",
"Health": 20,
"MovementSpeed": 1.0,
"Role": "MyPlugin:CustomCreatureRole",
"Tags": {
"Type": ["Monster", "Hostile"]
}
}| Property | Type | Description |
|---|---|---|
| LocalizedString | Entity name |
| String | Model asset reference |
| Float | Maximum health |
| String | AI role reference |
| String | Team/faction ID |
| Object | Classification tags |
| Property | Type | Default | Description |
|---|---|---|---|
| Float | 1.0 | Base move speed |
| Object | auto | Collision box |
| Float | 1.0 | Physics mass |
| Float | 1.0 | Gravity multiplier |
| Boolean | false | Can swim in water |
| Boolean | false | Can fly |
| Float | 0.5 | Max step-up height |
| Property | Type | Description |
|---|---|---|
| Float | Base attack damage |
| Float | Attacks per second |
| Float | Melee attack range |
| Float | Damage reduction |
| Float | Knockback reduction |
| Property | Type | Description |
|---|---|---|
| Float | Model scale |
| Float | Max render distance |
| Float | Shadow radius |
| Color | Outline glow color |
| String | Ambient particles |
my_creature_role.role{
"MotionController": "Walk",
"DefaultInstruction": {
"Sensor": {
"Type": "Always"
},
"BodyMotion": {
"Type": "Wander",
"Speed": 0.5,
"Radius": 10
},
"HeadMotion": {
"Type": "Nothing"
}
},
"Instructions": [
{
"Priority": 10,
"Sensor": {
"Type": "SensorPlayer",
"Range": 15,
"Condition": "Visible"
},
"BodyMotion": {
"Type": "Pursue",
"Target": "Player",
"Speed": 1.0
},
"HeadMotion": {
"Type": "Watch",
"Target": "Player"
},
"Actions": [
{
"Type": "Attack",
"Range": 2.0,
"Damage": 5,
"Cooldown": 1.0
}
]
}
]
}| Controller | Description |
|---|---|
| Ground-based movement |
| Aerial movement |
| Swimming movement |
| Stationary flight |
| Sensor | Description | Parameters |
|---|---|---|
| Always true | - |
| Always false | - |
| Random chance | |
| Detect players | |
| Detect entities | |
| When damaged | |
| Health check | |
| Time of day | |
| Navigation state | |
| Distance check | |
| Motion | Description | Parameters |
|---|---|---|
| Random wandering | |
| Chase target | |
| Run from target | |
| Go to position | |
| Move away | |
| Follow path | |
| Circle target | |
| Don't move | - |
| Start flying | - |
| Stop flying | - |
| Instant move | |
| Motion | Description | Parameters |
|---|---|---|
| Look at target | |
| Aim at target | |
| Look direction | |
| Don't control head | - |
| Action | Description | Parameters |
|---|---|---|
| Melee attack | |
| Projectile attack | |
| Apply effect | |
| Play sound | |
| Spawn entity | |
| Modify stat | |
| Set flag | |
| Trigger event | |
| Delay | |
{
"MotionController": "Walk",
"CombatRange": 2.0,
"AggroRange": 20,
"LeashRange": 40,
"DefaultInstruction": {
"Sensor": { "Type": "Always" },
"BodyMotion": {
"Type": "Wander",
"Speed": 0.4,
"Radius": 15,
"IdleTime": { "Min": 2, "Max": 5 }
},
"HeadMotion": { "Type": "Nothing" }
},
"Instructions": [
{
"Name": "ReturnToLeash",
"Priority": 100,
"Sensor": {
"Type": "SensorDistance",
"Target": "LeashPosition",
"Min": 40
},
"BodyMotion": {
"Type": "MoveTo",
"Target": "LeashPosition",
"Speed": 1.5
}
},
{
"Name": "AttackPlayer",
"Priority": 50,
"Sensor": {
"Type": "And",
"Sensors": [
{
"Type": "SensorPlayer",
"Range": 2.5,
"Condition": "Visible"
},
{
"Type": "SensorCooldown",
"Cooldown": "AttackCooldown",
"Ready": true
}
]
},
"BodyMotion": { "Type": "Stay" },
"HeadMotion": {
"Type": "Aim",
"Target": "Player"
},
"Actions": [
{
"Type": "Attack",
"Damage": 8,
"Animation": "attack_swing"
},
{
"Type": "SetCooldown",
"Cooldown": "AttackCooldown",
"Duration": 1.5
}
]
},
{
"Name": "ChasePlayer",
"Priority": 40,
"Sensor": {
"Type": "SensorPlayer",
"Range": 20,
"Condition": "Visible"
},
"BodyMotion": {
"Type": "Pursue",
"Target": "Player",
"Speed": 1.0,
"StopDistance": 1.5
},
"HeadMotion": {
"Type": "Watch",
"Target": "Player"
}
},
{
"Name": "FleeWhenLow",
"Priority": 60,
"Sensor": {
"Type": "And",
"Sensors": [
{ "Type": "SensorHealth", "Below": 0.25 },
{ "Type": "SensorPlayer", "Range": 15 }
]
},
"BodyMotion": {
"Type": "Flee",
"Target": "Player",
"Speed": 1.3,
"SafeDistance": 25
}
}
]
}my_creature_spawn.spawn{
"Entity": "MyPlugin:CustomCreature",
"SpawnWeight": 10,
"GroupSize": { "Min": 1, "Max": 3 },
"SpawnConditions": {
"Biomes": ["Plains", "Forest"],
"TimeOfDay": {
"Start": 0.75,
"End": 0.25
},
"LightLevel": { "Max": 7 },
"MoonPhase": ["Full", "Waning"],
"Weather": ["Clear", "Cloudy"],
"Surface": true
},
"SpawnCooldown": 300,
"MaxNearby": 4,
"NearbyCheckRadius": 32
}{
"Type": "Beacon",
"Entity": "MyPlugin:CustomCreature",
"SpawnRadius": 10,
"SpawnInterval": 100,
"MaxSpawns": 5,
"DespawnDistance": 64,
"RequiredBlock": "MyPlugin:SpawnerBlock"
}public class MyEntityData implements Component<EntityStore> {
public static final BuilderCodec<MyEntityData> CODEC = BuilderCodec.builder(
Codec.INT.required().fieldOf("Level"),
Codec.STRING.optionalFieldOf("CustomName", ""),
Codec.BOOL.optionalFieldOf("IsEnraged", false)
).constructor(MyEntityData::new);
private int level;
private String customName;
private boolean isEnraged;
public MyEntityData() {
this(1, "", false);
}
public MyEntityData(int level, String customName, boolean isEnraged) {
this.level = level;
this.customName = customName;
this.isEnraged = isEnraged;
}
// Getters and setters
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public String getCustomName() { return customName; }
public void setCustomName(String name) { this.customName = name; }
public boolean isEnraged() { return isEnraged; }
public void setEnraged(boolean enraged) { this.isEnraged = enraged; }
}@Override
protected void setup() {
ComponentType<EntityStore, MyEntityData> myDataType =
getEntityStoreRegistry().registerComponent(
MyEntityData.class,
"myPluginEntityData",
MyEntityData.CODEC
);
}public class EnrageSystem extends TickSystem<EntityStore> {
private ComponentAccess<EntityStore, MyEntityData> myData;
private ComponentAccess<EntityStore, HealthComponent> health;
@Override
protected void register(Store<EntityStore> store) {
myData = registerComponent(MyEntityData.class);
health = registerComponent(HealthComponent.class);
}
@Override
public void tick(
int index,
ArchetypeChunk<EntityStore> chunk,
Store<EntityStore> store,
CommandBuffer<EntityStore> buffer
) {
MyEntityData data = myData.get(chunk, index);
HealthComponent hp = health.getOptional(chunk, index);
if (hp != null && hp.getPercent() < 0.25f && !data.isEnraged()) {
data.setEnraged(true);
// Apply enrage buff
}
}
}public class MyDamageHandler extends EntityEventSystem<EntityStore, Damage> {
private ComponentAccess<EntityStore, MyEntityData> myData;
public MyDamageHandler() {
super(Damage.class);
}
@Override
protected void register(Store<EntityStore> store) {
myData = registerComponent(MyEntityData.class);
}
@Override
public void handle(
int index,
ArchetypeChunk<EntityStore> chunk,
Store<EntityStore> store,
CommandBuffer<EntityStore> buffer,
Damage damage
) {
MyEntityData data = myData.getOptional(chunk, index);
if (data != null && data.isEnraged()) {
// Reduce damage when enraged
damage.setAmount(damage.getAmount() * 0.5f);
}
}
}@Override
protected void setup() {
// Register components
ComponentType<EntityStore, MyEntityData> dataType =
getEntityStoreRegistry().registerComponent(
MyEntityData.class,
"myEntityData",
MyEntityData.CODEC
);
// Register systems
getEntityStoreRegistry().registerSystem(new EnrageSystem());
getEntityStoreRegistry().registerSystem(new MyDamageHandler());
// Register custom sensors
getCodecRegistry(Sensor.CODEC).register(
"MySensor", MySensor.class, MySensor.CODEC
);
// Register custom actions
getCodecRegistry(Action.CODEC).register(
"MyAction", MyAction.class, MyAction.CODEC
);
}{
"DisplayName": { "en-US": "Village Merchant" },
"Model": "MyPlugin/Models/merchant",
"Role": "MyPlugin:MerchantRole",
"IsInteractable": true,
"Interactions": {
"Use": "MyPlugin:OpenShop"
},
"DialogueTree": "MyPlugin:MerchantDialogue",
"Schedule": {
"06:00-18:00": "WorkAtShop",
"18:00-22:00": "Wander",
"22:00-06:00": "Sleep"
}
}{
"DisplayName": {
"en-US": "Shadow Guardian"
},
"Description": {
"en-US": "Ancient protector of the dark temple"
},
"Model": "MyPlugin/Models/shadow_guardian",
"Scale": 2.0,
"Health": 500,
"Armor": 10,
"AttackDamage": 20,
"MovementSpeed": 0.8,
"KnockbackResistance": 0.8,
"Role": "MyPlugin:ShadowGuardianRole",
"BossBar": {
"Enabled": true,
"Color": "Purple",
"Style": "Notched"
},
"Loot": "MyPlugin:ShadowGuardianLoot",
"DeathSound": "MyPlugin/Sounds/boss_death",
"AmbientSound": {
"Sound": "MyPlugin/Sounds/dark_ambient",
"Interval": 5
},
"Particles": "MyPlugin/Particles/shadow_aura",
"GlowColor": { "R": 0.5, "G": 0.0, "B": 0.8 },
"Tags": {
"Type": ["Boss", "Undead", "Hostile"]
}
}/npc debugreferences/entity-components.mdreferences/ai-sensors.mdreferences/ai-actions.md