Using Avatar Entities

Many abilities create an entity as part of the move; for example, the fireball ability creates a fireball entity that’s manipulated by the bender. All entities from abilities derive from the AvatarEntity class. Keep in mind AvatarEntity is only for ability related entities; mobs extend from other classes like EntityAnimal.

AvatarEntities all have an owner, which refers to who created the entity (using bending). Under normal conditions, the owner should always be set (though don’t count on this). They also sometimes have a controller, which refers to who currently controls the movement of the entity. Sometimes nobody is manipulating the entity’s movement, so the controller may not be set.

Nullability

Keep in mind that although the owner or controller of an AvatarEntity might be set, they may not be logged in, so an attempt to get the entity instance can return null.

Vectors

Unlike vanilla Entities, AvatarEntities integrate better with avatarmod vectors. Although vectors can already be used with vanilla entities, there are convenient methods to use Vectors for AvatarEntities:

  • Use position() and setPosition(vec) to manipulate the entity’s position
  • Use velocity() and setVelocity(vec) to manipulate the entity’s velocity. Velocity is in meters per second.

Warning

Don’t use the vanilla method getPosition() - this returns the BlockPos at the entity’s current position.

AvId and Lookup

Also unlike vanilla Entities, you can send references to an AvatarEntity over network. Vanilla entity Ids aren’t synchronized between server and client, so they can’t be used to synchronize entities. AvatarEntities have their own, synchronized Id which is called the AvId. The AvId is synchronized, so it can be used to send references across server and client.

If an entity needs to reference another entity, it’s easier to use SyncedEntity instead.

There are several lookup methods for AvatarEntities. They only return a single entity:

  • lookupEntity(world, id) - look up based on AvId
  • lookupEntity(world, cls, predicate) - look up based on a type, and filter entities out
  • lookupControlledEntity(world, cls, controller) - look up an entity which is controlled by the given entity

Hooks

AvatarEntities have many publicly callable hook methods. One example is the method onLargeWaterContact(), which should be called when the entity touches a large source of water. These hooks are available not only for convenience and conciseness, but also to promote interactions between different objects. Imagine a fire arc hits a water bubble. The water bubble calls onLargeWaterContact() on the fire arc, causing the fire to be extinguished. Hooks make interactions like this simple to implement without needing lots of special cases.

Hooks return true if the AvatarEntity was destroyed by the interaction (the AvatarEntity is responsible for calling setDead() on itself). For a list of hooks available to AvatarEntities, see the javadocs. You should call these hooks wherever appropriate.

Cookbook

Look up an AvatarEntity based on an id

int avId = /* ... */;
World world = /* ... */;

AvatarEntity avEnt = AvatarEntity.lookupEntity(world, avId);

Send the AvatarEntity flying towards their owner

AvatarEntity avEnt = /* ... */;

EntityLivingBase owner = avEnt.getOwner();
if (owner != null) {
  Vector ownerPos = Vector.getEntityPos(owner);
  Vector avEntPos = avEnt.position(); // can also use Vector.getEntityPos
  Vector direction = avEntPos.minus(ownerPos);

  Vector velocity = direction.normalize().times(10); // 10 m/s
  avEnt.addVelocity(velocity);
}

Creating Avatar Entities

The below information only is useful if you are editing a class extending AvatarEntity.

Collisions

AvatarEntities that wish to handle collisions should do so in the onCollidieWithEntity method.

Common operations

There are many operations that multiple AvatarEntities need to perform. These often are relatively simple yet involve a significant amount of code; for example breaking a block also involves dropping items, playing sounds, and creating particles. Several protected methods are available to simplify these operations:

  • breakBlock(pos) - break a block at the specified BlockPos
  • spawnExtinguishIndicators() - plays effects to indicate something is extinguished

Cookbook

Extinguish when hit water

@Override
public boolean onLargeWaterContact() {
  spawnExtinguishIndicators();
  setDead();
  return true;
}