
/*:
-------------------------------------------------------------------------------
@title HMS: Animated Faces
@author Hime --> HimeWorks (http://himeworks.com)
@version 1.1
@date Aug 12, 2020
@filename HIME_HMSAnimatedFaces.js
@url 

If you enjoy my work, consider supporting me on Patreon!

* https://www.patreon.com/himeworks

If you have any questions or concerns, you can contact me at any of
the following sites:

* Main Website: http://himeworks.com
* Facebook: https://www.facebook.com/himeworkscom/
* Twitter: https://twitter.com/HimeWorks
* Youtube: https://www.youtube.com/c/HimeWorks
* Tumblr: http://himeworks.tumblr.com/

-------------------------------------------------------------------------------
@plugindesc v1.0 - Allows you to use animated faces in your messages.
@help 
-------------------------------------------------------------------------------
== Description ==

RPG Maker let's you display a face in your message, but it's just a static
picture. With this plugin, you can create animated faces.

This plugin currently supports talking faces.

== Terms of Use ==

- Free for use in non-commercial projects with credits
- Free for use in commercial projects, but it would be nice to let me know
- Please provide credits to HimeWorks

== Change Log ==

* Aug 12, 2020
  1.1
    - fixed bug where old face wasn't cleared out before drawing new one
  1.0
    - initial release

== Usage ==

We use a "face animation data" file to keep track of face animations.
You can choose the name of this file in the plugin parameters, but by default
it's caled "face_animation_data.csv" and is located in the Data folder.

It's CSV format, which means you can set it up using spreadsheet software
like Microsoft Excel or Libra Sheets.

  -- Format --
  
This is the basic format

  Name,Type,Frames
  
Where

  Name - the filename
  Type - the expression
  Frames - number of frames in your animation

So for example, let's say I had a "Talking" animation for face image
Actor3 that has 4 frames. My file would look like

  Name,Type,Frames,Reference
  Actor3,Talking,4

Eventually there will be different types of face animations.

  -- Face Sheet Format --
  
For the actual animation images, we will be using the default RPG Maker
face format, which has 4 columns of frames per row. You can have as many
number of rows as you need.

The format of the face picture is the same as your regular face pictures:
each row has 4 frames. If you need more frames, just add more rows.

  F1 F2 F3 F4
  F5 F6 F7 F8
  F9 ...
  
The game will basically just loop through the frames depending on how many
frames you set in the Face Animation Data file.

-------------------------------------------------------------------------------
@param Filename
@desc Name of the face animation data file, in the Data folder. Case-sensitive
@default face_animation_data.csv
-------------------------------------------------------------------------------
*/ 
var Imported = Imported || {} ;
var HMS = HMS || {};
Imported.HMS = 1;
HMS.AnimatedMessageFaces = HMS.AnimatedMessageFaces || {}; 

(function ($) {
  
  $.params = PluginManager.parameters("HIME_HMSAnimatedFaces");
  $.filename = $.params["Filename"].trim();
    
  // holds face animation data
  
  DataManager._databaseFiles.push( 
    {name: '$dataFaceAnimations', src: $.filename },
  )
    
  var TH_DataManager_loadDataFile = DataManager.loadDataFile;
  DataManager.loadDataFile = function(name, src) {
    
    if (src === $.filename) {
      this.loadFaceDataFile(name, src);
    }
    else {
      TH_DataManager_loadDataFile.call(this, name, src)
    }    
  }
  
  DataManager.loadFaceDataFile = function(name, src) {
    var xhr = new XMLHttpRequest();
    var url = 'data/' + src;
    xhr.open('GET', url);
    xhr.onload = function() {
        if (xhr.status < 400) {
            window[name] = $.loadData(xhr.responseText);
            DataManager.onLoad(window[name]);
        }
    };
    xhr.onerror = this._mapLoader || function() {
        DataManager._errorUrl = DataManager._errorUrl || url;
    };
    window[name] = null;
    xhr.send();
  };
  
  $.loadData = function(resp) {
    var lines = resp.split(/\r?\n/);
    var data = {}
    for (var i = 1; i < lines.length; i++) {
      var tokens = lines[i].split(",")
      var entry = {}
      var name = tokens[0]
      var type = tokens[1]
      var frames = Math.floor(tokens[2]);
      
      entry[type] = {}
      entry[type].frames = frames;
      
      data[name] = entry;
    }
    return data;
  }
  
  // Get the number of frames for the given name and expression type
  $.getFrameCount = function(name, type) {
    var entries = $dataFaceAnimations[name]    
    var entry = entries[type]
    if (!entry) {
      throw "Unknown type %1 for image name %2".format(type, name)
    }    
    return entry.frames;
  }
  
  $.isAnimated = function(name) {
    return $dataFaceAnimations[name] !== undefined;
  }
    
  var TH_WindowMessage_processNormalCharacter = Window_Message.prototype.processNormalCharacter
  Window_Message.prototype.processNormalCharacter = function(textState) {
    TH_WindowMessage_processNormalCharacter.call(this, textState);
    this.updateAnimatedTalkingFace();
  };

  Window_Message.prototype.updateAnimatedTalkingFace = function() {        
    var name = $gameMessage.faceName();
    var entries = $dataFaceAnimations[name];
    if (!entries) {
      return;
    }
    var entry = entries["Talking"];
    if (entry) {
      var maxIndex = entry.frames;     
      this._animFaceIndex = this._animFaceIndex || 0
      this._animFaceIndex = (this._animFaceIndex + 0.15) // hardcode speed for now
          
      if (this._animFaceIndex >= maxIndex) {
        this._animFaceIndex = 0
      }
      
      
      var index = Math.floor(this._animFaceIndex) % maxIndex;  
      this.drawAnimatedFace($gameMessage.faceName(), index)
    }
  }
  
  Window_Message.prototype.drawAnimatedFace = function(name, index) {
    this.contents.clearRect(0, 0, Window_Base._faceWidth, Window_Base._faceHeight);
    this.drawFace(name, index, 0, 0)
  }
  
  var TH_WindowMessage_startWait = Window_Message.prototype.startWait;
  Window_Message.prototype.startWait = function(count) {
    TH_WindowMessage_startWait.call(this, count);
    
    // close mouth?
    
    var entries = $dataFaceAnimations[$gameMessage.faceName()];
    if (entries) {
      this._animFaceIndex = 0;
      this.drawAnimatedFace($gameMessage.faceName(), this._animFaceIndex)      
    }    
  }

})(HMS.AnimatedMessageFaces);