// DarkPlasma_PopupBattleOccurrence 1.0.0
// Copyright (c) 2020 DarkPlasma

/**
 * 2020/09/12 1.0.0 初回
 */

/*:ja
 * @plugindesc 戦闘中に発生した出来事をポップアップする
 * @author DarkPlasma
 * @license No License
 *
 * @target MZ
 *
 * @help
 * 戦闘中に発生した出来事をポップアップします。
 *
 * - HPの変化（ダメージ、回復）
 * - 弱点をついた/耐性を持っている
 * - ミス/クリティカル
 * - ステート/バフ状態になった
 */

(() => {
  'use strict';

  const DAMAGE_COLOR = {
    HP_DAMAGE: 0,
    HP_RECOVER: 1,
    MP_DAMAGE: 2,
    MP_RECOVER: 3,
    CRITICAL: 4,
    WEAKNESS: 5,
    RESIST: 6,
  };

  const _ColorManager_damageColor = ColorManager.damageColor;
  ColorManager.damageColor = function (colorType) {
    switch (colorType) {
      case DAMAGE_COLOR.CRITICAL:
        return '#F8F260';
      case DAMAGE_COLOR.WEAKNESS:
        return '#FFFFFF';
      case DAMAGE_COLOR.RESIST:
        return '#DCD6D0';
      default:
        return _ColorManager_damageColor.call(this, colorType);
    }
  };

  const _Game_Action_calcElementRate = Game_Action.prototype.calcElementRate;
  Game_Action.prototype.calcElementRate = function (target) {
    const rate = _Game_Action_calcElementRate.call(this, target);
    const result = target.result();
    result.elementRate = rate;
    return rate;
  };

  const _Game_ActionResult_clear = Game_ActionResult.prototype.clear;
  Game_ActionResult.prototype.clear = function () {
    _Game_ActionResult_clear.call(this);
    this.elementRate = 1;
  };

  const _Game_Battler_initMembers = Game_Battler.prototype.initMembers;
  Game_Battler.prototype.initMembers = function () {
    _Game_Battler_initMembers.call(this);
    this._statePopup = false;
    this._buffPopup = false;
  };

  Game_Battler.prototype.clearBuffPopup = function () {
    this._buffPopup = false;
  };

  Game_Battler.prototype.clearStatePopup = function () {
    this._statePopup = false;
  };

  Game_Battler.prototype.startBuffPopup = function () {
    this._buffPopup = true;
  };

  Game_Battler.prototype.startStatePopup = function () {
    this._statePopup = true;
  };

  Game_Battler.prototype.isBuffPopupRequested = function () {
    return this._buffPopup;
  };

  Game_Battler.prototype.isStatePopupRequested = function () {
    return this._statePopup;
  };

  const _Sprite_Battler_initMembers = Sprite_Battler.prototype.initMembers;
  Sprite_Battler.prototype.initMembers = function () {
    _Sprite_Battler_initMembers.call(this);
    this._buffSprites = [];
    this._stateSprites = [];
  };

  Sprite_Battler.prototype.createBuffSprite = function () {
    const last = this._buffSprites[this._buffSprites.length - 1];
    const sprite = new Sprite_BuffPopup();
    if (last) {
      sprite.duration = last.duration + 10;
    }
    sprite.x = this.x + this.damageOffsetX();
    sprite.y = this.y + this.damageOffsetY();
    sprite.setup(this._battler);
    this._buffSprites.push(sprite);
    this.parent.addChild(sprite);
  };

  Sprite_Battler.prototype.createStateSprite = function () {
    const last = this._stateSprites[this._stateSprites.length - 1];
    const sprite = new Sprite_StatePopup();
    if (last) {
      sprite.duration = last.duration + 10;
    }
    sprite.x = this.x + this.damageOffsetX();
    sprite.y = this.y + this.damageOffsetY();
    sprite.setup(this._battler);
    this._stateSprites.push(sprite);
    this.parent.addChild(sprite);
  };

  Sprite_Battler.prototype.destroyBuffSprite = function (sprite) {
    this.parent.removeChild(sprite);
    this._buffSprites.remove(sprite);
    sprite.destroy();
  };

  Sprite_Battler.prototype.destroyStateSprite = function (sprite) {
    this.parent.removeChild(sprite);
    this._stateSprites.remove(sprite);
    sprite.destroy();
  };

  const _Sprite_Battler_updateDamagePopup = Sprite_Battler.prototype.updateDamagePopup;
  Sprite_Battler.prototype.updateDamagePopup = function () {
    this.updateBuffPopup();
    this.updateStatePopup();
    _Sprite_Battler_updateDamagePopup.call(this);
  };

  Sprite_Battler.prototype.updateBuffPopup = function () {
    this.setupBuffPopup();
    if (this._buffSprites.length > 0) {
      this._buffSprites.forEach((sprite) => {
        sprite.update();
        if (!sprite.isPlaying()) {
          this.destroyBuffSprite(sprite);
        }
      });
    }
  };

  Sprite_Battler.prototype.updateStatePopup = function () {
    this.setupStatePopup();
    if (this._stateSprites.length > 0) {
      this._stateSprites.forEach((sprite) => {
        sprite.update();
        if (!sprite.isPlaying()) {
          this.destroyStateSprite(sprite);
        }
      });
    }
  };

  Sprite_Battler.prototype.setupBuffPopup = function () {
    if (this._battler.isBuffPopupRequested()) {
      if (this._battler.isSpriteVisible()) {
        this.createBuffSprite();
      }
      this._battler.clearBuffPopup();
    }
  };

  Sprite_Battler.prototype.setupStatePopup = function () {
    if (this._battler.isStatePopupRequested()) {
      if (this._battler.isSpriteVisible()) {
        this.createStateSprite();
      }
      this._battler.clearStatePopup();
    }
  };

  const _Sprite_Damage_initialize = Sprite_Damage.prototype.initialize;
  Sprite_Damage.prototype.initialize = function () {
    _Sprite_Damage_initialize.call(this);
    this._isWeakness = false;
    this._isResist = false;
    this._isCritical = false;
  };

  const _Sprite_Damage_setup = Sprite_Damage.prototype.setup;
  Sprite_Damage.prototype.setup = function (target) {
    const result = target.result();
    this._isCritical = result.critical;
    this._isWeakness = result.elementRate > 1;
    this._isResist = result.elementRate < 1;
    _Sprite_Damage_setup.call(this, target);
  };

  Sprite_Damage.prototype.createMiss = function () {
    const h = this.fontSize();
    const w = Math.floor(h * 3.0);
    const sprite = this.createChildSprite(w, h);
    sprite.bitmap.drawText('Miss', 0, 0, w, h, 'center');
    sprite.y = 0;
    sprite.ry = 0;
    sprite.baseY = 0;
    sprite.yVelocity = -4.8;
  };

  Sprite_Damage.prototype.createDigits = function (value) {
    let y = 0;
    if (this._isWeakness) {
      this.createTextSprite(y, 'WEAKNESS');
      this._colorType = DAMAGE_COLOR.WEAKNESS;
      y += this.fontSize();
    } else if (this._isResist) {
      this.createTextSprite(y, 'RESIST');
      this._colorType = DAMAGE_COLOR.RESIST;
      y += this.fontSize();
    }
    if (this._isCritical) {
      this.createTextSprite(y, 'CRITICAL');
      if (this._colorType < DAMAGE_COLOR.CRITICAL) {
        this._colorType = DAMAGE_COLOR.CRITICAL;
      }
      y += this.fontSize();
    }
    const height = this.fontSize();
    const textValue = Math.abs(value).toString();
    const width = this.textWidth(textValue);
    const sprite = this.createChildSprite(width, height);
    sprite.bitmap.drawText(textValue, 0, 0, width, height, 'center');
    sprite.y = y;
    sprite.ry = y;
    sprite.baseY = y;
    sprite.yVelocity = -4.8;
  };

  Sprite_Damage.prototype.createTextSprite = function (y, text) {
    const height = this.fontSize();
    const width = this.textWidth(text);
    const sprite = this.createChildSprite(width, height);
    sprite.bitmap.drawText(text, 0, 0, width, height, 'center');
    sprite.y = y;
    sprite.ry = y;
    sprite.baseY = y;
    sprite.yVelocity = -4.8;
  };

  Sprite_Damage.prototype.fontFace = function () {
    return 'Arial Black, Avenir-Black, sans-serif-black, GameFont';
  };

  Sprite_Damage.prototype.textWidth = function (text) {
    const height = this.fontSize();
    return Math.floor(height * text.length);
  };

  Sprite_Damage.prototype.outlineColor = function () {
    if (this._isWeakness) {
      return 'rgba(48,92,224,1.0)';
    } else if (this._isResist) {
      return 'rgba(64,64,64,1.0)';
    } else if (this._isCritical) {
      return 'rgba(200,28,32,1.0)';
    }
    return 'rgba(0,0,0,1.0)';
  };

  Sprite_Damage.prototype.updateChild = function (sprite) {
    sprite.y = Math.round(sprite.ry);
    sprite.ry += sprite.yVelocity;
    if (sprite.ry > sprite.baseY) {
      sprite.ry = sprite.baseY;
    }
    sprite.yVelocity += 0.3;
    sprite.setBlendColor(this._flashColor);
  };

  class Sprite_PopupRise extends Sprite_Damage {
    setup(target) {
      this._riseSpeed = 0.66;
    }

    damageColor() {
      return '#ffffff';
    }

    /**
     * @param {string} text 表示するテキスト
     * @param {number} iconIndex アイコンインデックス
     * @param {number} index 表示順インデックス
     */
    createIconAndSprite(text, iconIndex, index) {
      const height = this.fontSize();
      const width = this.textWidth(text) + ImageManager.iconWidth;
      const sprite = this.createChildSprite(width, height);
      const iconBitmap = ImageManager.loadSystem('IconSet');
      sprite.bitmap.blt(
        iconBitmap,
        (iconIndex % 16) * ImageManager.iconWidth,
        Math.floor(iconIndex / 16) * ImageManager.iconHeight,
        ImageManager.iconWidth,
        ImageManager.iconHeight,
        0,
        0
      );
      sprite.bitmap.drawText(text, ImageManager.iconWidth, 0, width, height, 'left');
      sprite.duration = this.defaultDuration() + Math.floor((index * height) / this._riseSpeed);
      this._duration = sprite.duration;
      sprite.opacity = 0;
      sprite.y = 0;
      sprite.ry = sprite.y;
    }

    defaultDuration() {
      return 90;
    }

    fadeInSpeed() {
      return 25;
    }

    fadeOutSpeed() {
      return 6;
    }

    updateChild = function (sprite) {
      sprite.duration--;
      if (sprite.duration > this.defaultDuration()) {
        return;
      }
      sprite.y = Math.round(sprite.ry);
      sprite.ry -= this._riseSpeed;
      if (sprite.duration > 0 && sprite.opacity < 255) {
        sprite.opacity += this.fadeInSpeed();
      } else if (sprite.duration < 0) {
        sprite.opacity -= this.fadeOutSpeed();
      }
      sprite.setBlendColor(this._flashColor);
    };
  }

  class Sprite_StatePopup extends Sprite_PopupRise {
    setup(target) {
      super.setup(target);
      const result = target.result();
      this._states = result.addedStateObjects();
      if (this._states.length > 0) {
        this.createStates(target);
      }
    }

    createStates(target) {
      const result = target.result();
      this._states = result.addedStateObjects();
      this._states.forEach((state, index) => {
        this.createIconAndSprite(state.name, state.iconIndex, index);
      });
    }
  }

  class Sprite_BuffPopup extends Sprite_PopupRise {
    setup(target) {
      super.setup(target);
      const result = target.result();
      /**
       * 同時に発生した場合、ステート数分だけ遅らせるために必要
       */
      this._statesCount = result.addedStateObjects().length;
      this._buffs = result.addedBuffs;
      this._debuffs = result.addedDebuffs;
      if (this._buffs.length > 0) {
        this.createBuffs(target);
      }
      if (this._debuffs.length > 0) {
        this.createDebuffs(target);
      }
    }

    createBuffs(target) {
      this._buffs.forEach((paramId, index) => {
        this.createIconAndSprite(
          TextManager.param(paramId),
          target.buffIconIndex(1, paramId),
          this._statesCount + index
        );
      });
    }

    createDebuffs(target) {
      this._debuffs.forEach((paramId, index) => {
        this.createIconAndSprite(
          TextManager.param(paramId),
          target.buffIconIndex(-1, paramId),
          this._statesCount + this._buffs.length + index
        );
      });
    }
  }

  const _Window_BattleLog_displayAddedStates = Window_BattleLog.prototype.displayAddedStates;
  Window_BattleLog.prototype.displayAddedStates = function (target) {
    _Window_BattleLog_displayAddedStates.call(this, target);
    const addedStates = target.result().addedStateObjects();
    /**
     * 戦闘不能時はポップアップなし
     */
    if (!addedStates.some((state) => state.id === target.deathStateId()) && addedStates.length > 0) {
      target.startStatePopup();
    }
  };

  const _Window_BattleLog_displayChangedBuffs = Window_BattleLog.prototype.displayChangedBuffs;
  Window_BattleLog.prototype.displayChangedBuffs = function (target) {
    _Window_BattleLog_displayChangedBuffs.call(this, target);
    const result = target.result();
    if (result.addedBuffs.length > 0 || result.addedDebuffs.length > 0) {
      target.startBuffPopup();
    }
  };
})();
