protected MovableComponent getMovable(Cell cell) {
   MovableComponent mc = cell.getComponent(MovableComponent.class);
   if (mc != null) {
     return mc;
   } else {
     addMovableComponent(cell);
     return cell.getComponent(MovableComponent.class);
   }
 }
  public AvatarImiJME(Cell cell) {
    super(cell);
    assert (cell != null);
    final Cell c = cell;

    // Listen for avatar configuration changes.
    AvatarConfigComponent comp = cell.getComponent(AvatarConfigComponent.class);
    comp.addAvatarConfigChangeListener(new AvatarChangeListener());

    // XXX NPC HACK XXX
    if (cell instanceof AvatarCell) username = ((AvatarCell) cell).getIdentity().getUsername();
    else username = "******"; // HACK !

    characterMotionListener =
        new CharacterMotionListener() {
          Vector3f prevTrans;
          PMatrix prevRot;
          float prevHeight;
          boolean prevCollision;

          public void transformUpdate(Vector3f translation, PMatrix rotation) {
            if (logger.isLoggable(Level.FINEST)) {
              logger.finest(
                  "Transform update: translation: prev: "
                      + prevTrans
                      + " cur: "
                      + translation
                      + " rotation: prev: "
                      + prevRot
                      + " cur: "
                      + rotation);
            }

            float height = avatarCharacter.getController().getHeight();
            boolean collision = avatarCharacter.getController().isColliding();

            if (prevTrans == null
                || !Math3DUtils.epsilonEquals(prevTrans, translation, 0.001f)
                || prevRot == null
                || !prevRot.epsilonEquals(rotation, 0.001f)
                || !Math3DUtils.epsilonEquals(prevHeight, height, 0.001f)
                || prevCollision != collision) {
              MovableAvatarComponent mac =
                  ((MovableAvatarComponent) c.getComponent(MovableComponent.class));
              mac.localMoveRequest(
                  new CellTransform(rotation.getRotation(), translation), height, collision);

              prevTrans = translation.clone();
              prevRot = new PMatrix(rotation);
              prevHeight = height;
              prevCollision = collision;
            }
          };
        };

    // This info will be sent to the other clients to animate the avatar
    gameContextListener =
        new GameContextListener() {

          public void trigger(
              boolean pressed, int trigger, Vector3f translation, Quaternion rotation) {
            synchronized (this) {
              currentTrigger = trigger;
              currentPressed = pressed;
            }

            String animationName = null;

            // OWL issue #237 - regardless of the current state, send the
            // animation that is currently set in CycleActionState. This
            // is consistent with the behavior of
            // WlAvatarContext.setMiscAnimation() used in the trigger()
            // method below
            CycleActionState cas =
                (CycleActionState) avatarCharacter.getContext().getState(CycleActionState.class);
            if (cas != null) {
              animationName = cas.getAnimationName();
            }

            float height = avatarCharacter.getController().getHeight();
            boolean collision = avatarCharacter.getController().isColliding();

            if (c.getComponent(MovableComponent.class) == null) {
              logger.warning("!!!! NULL MovableComponent");
            } else {
              MovableAvatarComponent mac =
                  ((MovableAvatarComponent) c.getComponent(MovableComponent.class));
              mac.localMoveRequest(
                  new CellTransform(rotation, translation),
                  trigger,
                  pressed,
                  animationName,
                  height,
                  collision,
                  null);
            }
          }
        };
  }