/*:
 * @target MZ
 * @plugindesc [改良版] ショップ画面拡張 + HP/MP比較 + 装備％補正(特徴)を新値に反映
 * @author you
 *
 * @help
 * 右側の新値（→の右）は、装備の固定加算(params)に加え、
 * 防具/武器の「特徴」(Traits: PARAM) による％補正も掛け合わせた
 * 最終実数値を表示します。
 * 例：防具に「最大HP×105%」がある場合、装備後HPは1.05倍で表示。
 *
 * 既存の ShopScene_Extension v1.0.5 の下に配置してください。
 */

(() => {
"use strict";

/** ───────── ユーティリティ（装備による加算/倍率の分解） ───────── **/

// 装備配列から、params 加算合計を取得
function sumEquipParams(equips, paramId) {
  return equips.reduce((sum, it) => {
    return sum + (it && it.params ? (it.params[paramId] || 0) : 0);
  }, 0);
}

// 装備配列から、Traits: PARAM の倍率（乗算）を取得
function prodEquipParamRate(equips, paramId) {
  // Game_BattlerBase の定数に合わせた code
  const TRAIT_PARAM = Game_BattlerBase.TRAIT_PARAM; // 通常は 21
  return equips.reduce((prod, it) => {
    if (!it || !it.traits) return prod;
    const r = it.traits.reduce((p, tr) => {
      return p * (tr.code === TRAIT_PARAM && tr.dataId === paramId ? tr.value : 1);
    }, 1);
    return prod * r;
  }, 1);
}

// 指定アクターの「非装備由来」の加算・倍率を算出（＝現在から装備分を差し引く）
function decomposeNonEquipPart(actor, paramId) {
  const equips = actor.equips();
  const plusAll = actor.paramPlus(paramId);                 // 装備を含む全加算
  const plusEquips = sumEquipParams(equips, paramId);       // 装備由来の加算
  const rateAll = actor.paramRate(paramId);                 // 装備を含む全倍率（Traits総積）
  const rateEquips = prodEquipParamRate(equips, paramId);   // 装備由来の倍率（Traits PARAM のみ）
  const nonEquipPlus = plusAll - plusEquips;                // 装備以外の加算
  const nonEquipRate = rateEquips !== 0 ? (rateAll / rateEquips) : rateAll; // 装備以外の倍率
  return { nonEquipPlus, nonEquipRate };
}

// 仮想的に「old 装備 → new 装備」に差し替えたときの装備配列を作る
function virtualEquips(actor, newItem, oldItem) {
  const equips = actor.equips().slice();
  if (!newItem && !oldItem) return equips;

  // 差し替え対象スロットを特定
  let slotIndex = -1;

  if (oldItem) {
    // 現在装備している oldItem のスロットを探す
    slotIndex = equips.findIndex(e => e && e.objectId === oldItem.objectId && e.etypeId === oldItem.etypeId);
    // objectId 無いDBでは id で同定
    if (slotIndex < 0) slotIndex = equips.findIndex(e => e && e.id === oldItem.id && e.etypeId === oldItem.etypeId);
  }

  if (slotIndex < 0 && newItem) {
    // newItem の etypeId に合うスロットを探す（最初に見つかった箇所）
    const etypeId = newItem.etypeId;
    const slots = actor.equipSlots();
    slotIndex = slots.findIndex(s => s === etypeId);
    // 既に同スロットに何か入っている場合も素直に置き換える
    if (slotIndex < 0) slotIndex = 0; // 念のため
  }

  if (slotIndex >= 0) {
    equips[slotIndex] = newItem || null; // new を入れる（外す場合は null）
  }
  return equips;
}

/** ───────── ここから表示ロジックの上書き/拡張 ───────── **/

// 8パラメータ（HP/MP含む）を描画
const _drawAllParams = Window_ShopStatus.prototype.drawAllParams;
Window_ShopStatus.prototype.drawAllParams = function(x, y) {
  for (let i = 0; i <= 7; i++) {
    this.drawItem(x, y, i);
    y += this.lineHeight();
  }
};

// 「右側の新値」を、装備の固定加算＋Traits倍率を反映した最終値で計算
Window_ShopStatus.prototype.newEquipValue = function(actor, paramId, newItem, oldItem) {
  // 現在の装備のうち「装備由来以外」の加算/倍率をまず取り出す
  const { nonEquipPlus, nonEquipRate } = decomposeNonEquipPart(actor, paramId);

  // 差し替え後の仮想装備リストを作る
  const vEquips = virtualEquips(actor, newItem, oldItem);

  // 仮想装備由来の加算＆倍率
  const plusEquips = sumEquipParams(vEquips, paramId);
  const rateEquips = prodEquipParamRate(vEquips, paramId);

  // ベース値（職業・レベル等）
  const base = actor.paramBase(paramId);
  // バフ（ショップ中は通常0だが、残っていても左/右で整合が取れるよう適用）
  const buffRate = actor.paramBuffRate(paramId);

  // 公式計算式に近い形で算出（非装備と装備の合成）
  // 注：MZの内部実装と小数点処理の順序が完全一致しないケースもありますが、
  //     実用上ほぼ一致するようにしています。
  let value = (base) * (nonEquipRate * rateEquips) + (nonEquipPlus + plusEquips);
  value = Math.floor(value * buffRate);

  // 最小/最大クリップ
  value = value.clamp(actor.paramMin(paramId), actor.paramMax(paramId));
  return value;
};

// 右側の描画も既存の仕組みでOK（newEquipValue を差し替えたので％補正が反映される）
const _drawNewParam = Window_ShopStatus.prototype.drawNewParam;
Window_ShopStatus.prototype.drawNewParam = function(x, y, paramId) {
  const paramWidth = this.paramWidth();
  if (this._actor.canEquip(this._item)) {
    const newValue = this.newEquipValue(this._actor, paramId, this._item, this._currentEquippedItem);
    const diff = newValue - this._actor.param(paramId);
    this.changeTextColor(ColorManager.paramchangeTextColor(diff));
    this.drawText(newValue, x, y, paramWidth, "right");
  } else {
    this.resetTextColor();
    this.changePaintOpacity(false);
    this.drawText("--", x, y, paramWidth, "right");
    this.changePaintOpacity(true);
  }
};

})();
