Source: ui/avatar-controller.js

/**
This is remote control for user's avatar. Installed as change listener to WorldManager, tracks position of all events that user 
sends - typically movement - and optinally adds some more - typically avatar animations.
 */
export class AvatarController {
  constructor( worldManager, avatar ) {
    /** Name of idle animation */
    this.idle = null;
    /** Name of walk animation */
    this.walk = null;
    /** Timestamp of last change */
    this.lastChange = Date.now();
    /** Idle timeout, default 10s */
    this.idleTimeout = 200;
    this.lastAnimation = null;
    this.worldManager = worldManager;
    this.animations = [];
    var groups = avatar.getAnimationGroups();
    groups.forEach( group => this.animations.push(group.name));
    this.otherAnimations = [];
    this.processAnimations();
    this.setupIdleTimer();
  }
  
  processAnimations() {
    this.animations.forEach( a => {
      console.log(a);
      var name = a.toLowerCase();
      if ( name.indexOf('walk') >= 0 ) {
        this.walk = a;
      } else if ( name.indexOf('idle') >= 0 ) {
        this.idle = a;
      } else {
        this.otherAnimations.push(a);
      }
    });
  }
  setupIdleTimer() {
    if ( this.idleTimerId ) {
      return;
    }
    this.idleTimerId = setInterval(() => {
      if ( this.worldManager.isOnline() && Date.now() - this.lastChange > this.idleTimeout ) {
        clearInterval(this.idleTimerId);
        this.idleTimerId = null;
        this.sendAnimation(this.idle);
      }
    }, this.idleTimeout);
  }
  sendAnimation(name, loop=false) {
    if ( this.animations.includes(name) && name != this.lastAnimation ) {
      this.worldManager.sendMy({animation:{name:name,loop:loop}});
      this.lastAnimation = name;
    }
  }
  /**
  Process locally generated changes to avatar. Called from WorldManager.trackChanges().
  @param changes array of field,value object pairs
   */
  processChanges(changes) {
    if ( this.worldManager.world.inXR ) {
      // do NOT send anything while in XR
      return;
    }
    for ( var change of changes ) {
      this.lastChange = Date.now();
      if ( change.field == "position" ) {
        this.setupIdleTimer();
        this.sendAnimation(this.walk,true);
        break;
      } else if ( change.field == "rotation") {
        // CHECKME anything?
      } else if ( change.field == "wrote") {
        // process text and try to find some meaninful animation
        var words = change.value.split(' ');
        for ( var word of words ) {
          if ( word.length > 1 ) {
            var match = this.otherAnimations.find( e => e.includes(word.toLowerCase()));
            if ( match ) {
              this.sendAnimation(match);
              break;
            }
          }
        }
      }
    }
  }
  
}