HyCodeYourTale

Entities & NPCs

Entities & NPCs

Dokumentace k práci s entitami a NPC v Hytale.

Obsah

| Soubor | Popis |
|--------|-------|
| ENTITY_CREATION.md | Entity hierarchie, Holder pattern, komponenty, spawn |
| NPC_BEHAVIOR.md | NPCPlugin, Role, Instruction, Sensor, Motion, Action |

---

Entity Přehled

V Hytale jsou entity reprezentovány jako ECS entity s komponentami:

Entity = ID + Komponenty

Příklad Player entity:
├── Player
├── PlayerRef
├── TransformComponent
├── HeadRotation
├── NetworkId
├── BoundingBox
└── ... další komponenty

---

Vytvoření Entity

Holder Pattern

public Holder createCustomEntity(Store store, Vector3d position) {
// Vytvoř holder pro novou entitu
Holder holder = EntityStore.REGISTRY.newHolder();

// Pozice a rotace
holder.addComponent(
TransformComponent.getComponentType(),
new TransformComponent(position, new Vector3f(0, 0, 0))
);

// Network ID (pro síťovou synchronizaci)
holder.addComponent(
NetworkId.getComponentType(),
new NetworkId(store.getExternalData().takeNextNetworkId())
);

// Bounding box
holder.addComponent(
BoundingBox.getComponentType(),
new BoundingBox(/ bounding box data /)
);

return holder;
}

Spawn do Světa

public void spawnEntity(World world, Holder holder) {
world.execute(() -> {
Store store = world.getEntityStore().getStore();
store.addEntity(holder, AddReason.SPAWN);
});
}

---

Entity s Modelem

Z TeleportPlugin - vytvoření warp markeru:

@Nonnull
public Holder createWarp(@Nonnull Warp warp, @Nonnull Store store) {
Transform transform = warp.getTransform();
Holder holder = EntityStore.REGISTRY.newHolder();

// Pozice
holder.addComponent(
TransformComponent.getComponentType(),
new TransformComponent(transform.getPosition(), transform.getRotation())
);

// Network ID
holder.addComponent(
NetworkId.getComponentType(),
new NetworkId(store.getExternalData().takeNextNetworkId())
);

// Nelze interagovat
holder.ensureComponent(Intangible.getComponentType());

// Model
holder.addComponent(
ModelComponent.getComponentType(),
new ModelComponent(this.warpModel)
);

// Bounding box z modelu
holder.addComponent(
BoundingBox.getComponentType(),
new BoundingBox(this.warpModel.getBoundingBox())
);

// Jmenovka
holder.addComponent(
Nameplate.getComponentType(),
new Nameplate(warp.getId())
);

// Skrytá v adventure módu
holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType());

// Neserializovat
holder.ensureComponent(EntityStore.REGISTRY.getNonSerializedComponentType());

return holder;
}

---

Entity Komponenty

Základní Komponenty

| Komponenta | Popis |
|------------|-------|
| TransformComponent | Pozice a rotace |
| NetworkId | ID pro síťovou synchronizaci |
| BoundingBox | Kolizní box |
| ModelComponent | 3D model |
| Nameplate | Jmenovka nad entitou |

Speciální Komponenty

| Komponenta | Popis |
|------------|-------|
| Intangible | Nelze s entitou interagovat |
| HiddenFromAdventurePlayers | Skrytá pro adventure hráče |
| MovementStatesComponent | Stav pohybu |
| EffectControllerComponent | Aktivní efekty |
| ProjectileComponent | Projektil |

---

NPC Definice (Assets)

NPC jsou definovány v Assets/Server/NPC/:

{
"Id": "merchant",
"Name": "Merchant",
"Model": "npc_merchant",
"Health": 100,
"Behavior": {
"Type": "Passive",
"Interactions": [
{
"Type": "Trade",
"TradeList": "merchant_trades"
}
]
},
"Dialogue": {
"Greeting": "dialogue.merchant.greeting",
"Farewell": "dialogue.merchant.farewell"
}
}

---

Entity Definice (Assets)

Entity v Assets/Server/Entity/:

{
"Id": "zombie",
"Name": "Zombie",
"Model": "mob_zombie",
"Health": 20,
"Damage": 3,
"MovementSpeed": 0.23,
"Behavior": {
"Type": "Hostile",
"TargetSelector": "NearestPlayer",
"AttackRange": 2.0
},
"Drops": "zombie_drops",
"SpawnConditions": {
"LightLevel": { "Max": 7 },
"Biomes": ["plains", "forest", "desert"]
}
}

---

Spawn při Načtení Chunku

private void onChunkPreLoadProcess(ChunkPreLoadProcessEvent event) {
WorldChunk chunk = event.getChunk();
BlockChunk blockChunk = chunk.getBlockChunk();

if (blockChunk == null) return;

int chunkX = blockChunk.getX();
int chunkZ = blockChunk.getZ();
World world = chunk.getWorld();

// Zkontroluj jestli má tento chunk naše entity
for (MyEntity entity : getEntitiesForChunk(chunkX, chunkZ)) {
Vector3d position = entity.getPosition();

// Ověř že pozice je v tomto chunku
if (ChunkUtil.isInsideChunk(chunkX, chunkZ,
MathUtil.floor(position.x),
MathUtil.floor(position.z))) {

// Spawn na world threadu
world.execute(() -> {
Store store = world.getEntityStore().getStore();
Holder holder = createEntityHolder(entity, store);
store.addEntity(holder, AddReason.LOAD);
});
}
}
}

---

EntityRemoveEvent

public class EntityRemoveSystem extends EntityEventSystem {

public EntityRemoveSystem() {
super(EntityRemoveEvent.class);
}

@Override
public void handle(int i, ArchetypeChunk chunk,
Store store, CommandBuffer buffer,
EntityRemoveEvent event) {

Ref ref = chunk.getReferenceTo(i);

// Entita je odstraňována ze světa
getLogger().atInfo().log("Entity removed");
}

@Override
public Query getQuery() {
return null; // Všechny entity
}
}

---

Práce s Existujícími Entitami

Získání Entity Reference

// Z eventu
Ref ref = chunk.getReferenceTo(i);

// Z hráče
Ref playerRef = player.getRef();

// Přes store
// (závisí na konkrétním API)

Modifikace Entity

world.execute(() -> {
Ref ref = entity.getRef();
Store store = ref.getStore();

// Změna pozice
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (transform != null) {
transform.setPosition(newPosition);
store.setComponent(ref, TransformComponent.getComponentType(), transform);
}

// Přidání komponenty
store.addComponent(ref, MyComponent.getComponentType(), new MyComponent());

// Odebrání komponenty
store.removeComponent(ref, SomeComponent.getComponentType());
});

Zničení Entity

// Přes CommandBuffer (v systému)
commandBuffer.destroyEntity(ref);

// Nebo přes store
// store.removeEntity(ref);

---

UUID a Identifikace

UUIDComponent

// Získání UUID entity
UUIDComponent uuidComp = store.getComponent(ref, UUIDComponent.getComponentType());
if (uuidComp != null) {
UUID entityUuid = uuidComp.getUuid();
}

NetworkId

// Pro síťovou identifikaci
NetworkId networkId = store.getComponent(ref, NetworkId.getComponentType());
int id = networkId.getId();

---

Shrnutí

| Operace | Metoda |
|---------|--------|
| Vytvořit holder | EntityStore.REGISTRY.newHolder() |
| Přidat komponentu | holder.addComponent(type, component) |
| Zajistit komponentu | holder.ensureComponent(type) |
| Spawn entity | store.addEntity(holder, AddReason.SPAWN) |
| Získat network ID | store.getExternalData().takeNextNetworkId() |
| Zničit entity | commandBuffer.destroyEntity(ref) |

| AddReason | Použití |
|-----------|---------|
| SPAWN | Nově vytvořená entita |
| LOAD | Načtená entita (z chunku) |

Last updated: 20. ledna 2026