//=============================================================================
// RPG Maker MZ - IM_BattleStateWindow
//
// Copyright 2025 I'm moralist/min-pub.com All rights reserved.
// This source code or any portion thereof must not be
// reproduced or used without licensed in any manner whatsoever.
//=============================================================================
/*:
 * @target MZ
 * @plugindesc Damage-Formula Pack - puts lots of easy-to-read helpers in one place
 * @author U-mu
 *
 * @help
 * ■ 使い方
 * スキルの「ダメージ計算式」に
 *     this.magicRateDamage(a, b, 1)
 * と記述してください。
 * 第3引数は MAT／MDF に掛ける倍率で、省略時は 1 になります
 *
 * ・基本娼婦スキルダメージ (倍率指定可)	
 * this.prostituteDmg(a, b, 1)	引数には倍率を入れる
 *・敵：発情値上昇攻撃	
 * this.estrusDmg(b, 1)	引数には倍率を入れる(20以上のダメージでアクメ)
 * ・敵：追加発情値ダメージ(HPダメージと併用想定)	
 * this,addEstrusDmg(b, 1)	引数には倍率を入れる(20以上のダメージでアクメ)
 */

(() => {
  'use strict';

  /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   * ▼ ADD YOUR FORMULAS ▼
   * ---------------------------------------------------------------
   * テンプレート:
   * Game_Action.prototype.<yourFuncName> = function (b) {
   *   const a = this.subject();                // 攻撃側
   *   // …計算…
   *   return result;
   * };
   * ※命名は “動詞＋条件” など分かりやすい英単語推奨
   *   MAT と MDF に任意倍率を掛けた魔法ダメージを計算し、
   *   20 以上なら共通イベント 1552 を予約して返す。
   *
   * @param {Game_Battler} a 使用者
   * @param {Game_Battler} b 対象
   * @param {Number} [rate=2.6] 乗算倍率
   * @returns {Number} ダメージ値（0 以上）
   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

  /** ----------------------------------------------------------
   * prostituteDmg 基本娼婦スキルダメージ (倍率指定可)
   * ----------------------------------------------------------*/
  Game_Action.prototype.prostituteDmg = function (a, b, rate = 1) {
   rate = Number(rate) || 1;

  /** ① 基礎ダメージ：MAT と MDF の差 × 任意倍率 */
   const base = (a.mat - b.mdf) * rate;

  /** ② 使用者の攻撃側属性補正を乗算（敵側は掛けない）*/
   const eid        = this.item().damage.elementId;  // スキルの属性 ID
   const atkERate   = eid >= 0 ? a.elementRate(eid)  // 使用者だけ
                              : 1;                  // 無属性なら 1

  /** ③ 最終値（負なら 0）を返す */
   return Math.max(Math.round(base * atkERate), 0);
};
  
/* ----------------------------------------------------------
 * estrusDmg  敵：発情値上昇ダメージ
 *   基礎 (a.mat - b.mdf) × rate
 *   ＋ 対象の属性 ID 9 有効度を乗算
 * ----------------------------------------------------------*/
Game_Action.prototype.estrusDmg = function (b, rate = 1) {
  rate = Number(rate) || 1;
  const a       = this.subject();
  const attr9   = b.elementRate(9);                 // ★ターゲットの属性9
  const raw     = (a.mat - b.mdf) * rate * attr9;   // ←ここで掛ける
  const dmg     = Math.max(Math.round(raw), 1);

  $gameVariables.setValue(895, dmg);                // デバッグ用など
  return dmg;                                       // → あとは標準処理
};

/* ----------------------------------------------------------
 * addEstrusDmg  敵：追加発情 TP ダメージ
 *   基礎 (a.mat - b.mdf) × rate
 *   ＋ 対象の属性 ID 9 有効度を乗算
 *   TP だけ加算し、HP ダメージは返さない
 * ----------------------------------------------------------*/
Game_Action.prototype.addEstrusDmg = function (b, rate = 1) {
  rate = Number(rate) || 1;
  const a       = this.subject();
  const attr9   = b.elementRate(9);                     // ★ターゲットの属性9
  const tpRaw   = (a.mat - b.mdf) * rate * attr9;       // ←ここで掛ける
  const tpGain  = Math.max(Math.round(tpRaw), 0);

  if (tpGain > 0) {
    b.gainTp(tpGain);                                   // TP 増加
    $gameVariables.setValue(895, tpGain);               // 任意で保存
  }
  return 0;                                             // ダメージ併用用
};
  

  /* ------------------------------------------------------------
 * 通常攻撃   (a.atk - b.def) × rate
 * ---------------------------------------------------------- */
Game_Action.prototype.AtkDmg = function (b, rate = 1) {
  rate = isNaN(rate) ? 1 : Number(rate);
  const a   = this.subject();
  const raw = (a.atk - b.def) * rate;
  return Math.max(Math.round(raw), 0);
};

/* ------------------------------------------------------------
 * 脆弱な部位への攻撃   (a.atk - b.def*0.5) × rate
 * ---------------------------------------------------------- */
Game_Action.prototype.weakDmg = function (b, rate = 1) {
  rate = isNaN(rate) ? 1 : Number(rate);
  const a   = this.subject();
  const raw = (a.atk - b.def * 0.5) * rate;
  const dmg = Math.max(Math.round(raw), 0);

  // ★ バトルメッセージ
  if (BattleManager && BattleManager._logWindow) {
    BattleManager._logWindow.addText("脆弱な部位への一撃！ 防御力の50％が無視される");
  }
  return dmg;
};

/* ------------------------------------------------------------
 * 急所攻撃   a.atk × rate
 * ---------------------------------------------------------- */
Game_Action.prototype.vitalDmg = function (b, rate = 1) {
  rate = isNaN(rate) ? 1 : Number(rate);
  const a   = this.subject();
  const raw = a.atk * rate;
  const dmg = Math.max(Math.round(raw), 0);

  // ★ バトルメッセージ
  if (BattleManager && BattleManager._logWindow) {
    BattleManager._logWindow.addText("防御力無視の急所攻撃！");
  }
  return dmg;
};

/* ------------------------------------------------------------
 * 挿入　
 *  ※戻り値 0 なので、後ろに通常ダメージ式を ; で続けられます
 * ---------------------------------------------------------- */
const STATE_ENEMY_INSERT = 40;                  // ステートID：挿入（敵に付与する挿入してる状態のステート）
const STATE_BLANC_HOLE = 10;                    // ステートID：無防備な淫穴
const VARIABLE_INSERTABLE = 893;                // 変数ID：挿入可能人数
const VARIABLE_MAX_INSERTABLE = 873;            // 変数ID：最大挿入可能人数
const VARIABLE_CURRENT_INSERTED = 874;          // 変数ID：現在挿入人数
const SWITCH_INSERTABLE = 810;                  // スイッチID：無防備な淫穴
const RES_FAILED_INSERT = "0900-064-n064-0";    // リソースラベル：「失敗！ 敵はシルシェに挿入できなかった！」
const RES_SUCCESS_INSERT = "0900-066-n066-0";   // リソースラベル：「シルシェは挿入されてしまった！」
const RES_REACTION_VAGINAL = "0990-141-n141-0"; // リソースラベル：おまんこ挿入リアクション
const RES_REACTION_ANAL = "0990-142-n142-0";    // リソースラベル：アナル挿入リアクション
const RES_REACTION_ORAL = "0990-143-n143-0";    // リソースラベル：おクチ挿入リアクション
const STATE_INSERTED_SINGLE = 197;              // ステートID：挿入
const STATE_INSERTED_DOUBLE = 198;              // ステートID：二つ穴挿入
const STATE_INSERTED_TRIPLE = 199;              // ステートID：三つ穴挿入
const EVENT_INSERT_VAGINAL = 1834;              // コモンイベント：演出_挿入(おまんこ)
const EVENT_INSERT_ORAL = 1889;                 // コモンイベント：演出_挿入(おクチ)
const EVENT_INSERT_ANAL = 1890;                 // コモンイベント：演出_挿入(ケツ穴)

Game_Action.prototype.insertion = function (b) {
  const buttler = this.subject();     // 対象のバトラー（この場合敵）
  const actor = $gameActors.actor(1); // シルシェ
  const v = $gameVariables;
  const s = $gameSwitches;

  const inserted = buttler.stateLevel(STATE_ENEMY_INSERT);
  if (inserted > 0) {
    // 既に挿入済み
    if (BattleManager && BattleManager._logWindow && DebugSwitch.isEnable()) {
      BattleManager._logWindow.addText("デバッグ:挿入試行, 既に挿入済");
    }
  } else {
    const blackHoles = actor.stateLevel(STATE_BLANC_HOLE);
    const maxHoles = v.value(VARIABLE_MAX_INSERTABLE);
    if (blackHoles > 0 && blackHoles <= maxHoles) {
      // 自分に挿入ステートを付与
      buttler.addState(STATE_ENEMY_INSERT);

      // シルシェに挿入処理
      actor.removeState(STATE_BLANC_HOLE);  // 無防備な淫穴 - 1

      const single = actor.stateLevel(STATE_INSERTED_SINGLE);
      const double = actor.stateLevel(STATE_INSERTED_DOUBLE);
      const triple = actor.stateLevel(STATE_INSERTED_TRIPLE);

      let remainHole = 0;
      if (triple > 0) {
        // もう既に三つ穴挿入がある＝無防備な淫穴と不整合してるのでバグ
        if (BattleManager && BattleManager._logWindow && DebugSwitch.isEnable()) {
          BattleManager._logWindow.addText(`デバッグ:挿入試行, 挿入可能な穴${blackHoles}に対し挿入状態が3`);
        }
      } else if (double > 0) {
        // 三つ目（ケツ穴）
        actor.removeState(STATE_INSERTED_DOUBLE);
        actor.addState(STATE_INSERTED_TRIPLE);

        // 挿入カットイン
        $gameTemp.reserveCommonEvent(EVENT_INSERT_ANAL);

        // シルシェの反応
        if (!!$gameTemp.logWindowController) {
          $gameTemp.logWindowController.addLog(RES_REACTION_ANAL, null, '戦闘ログ');
        }

        // 一応変数系も更新しておく
        v.setValue(VARIABLE_CURRENT_INSERTED, 3);
        remainHole = 0;
      } else if (single > 0) {
        // 二つ目（おクチ）
        actor.removeState(STATE_INSERTED_SINGLE);
        actor.addState(STATE_INSERTED_DOUBLE);

        // 挿入カットイン
        $gameTemp.reserveCommonEvent(EVENT_INSERT_ORAL);

        // シルシェの反応
        if (!!$gameTemp.logWindowController) {
          $gameTemp.logWindowController.addLog(RES_REACTION_ORAL, null, '戦闘ログ');
        }

        // 一応変数系も更新しておく
        v.setValue(VARIABLE_CURRENT_INSERTED, 2);
        remainHole = Math.min(1, maxHoles - 1);
      } else {
        // 一つ目（オマンコ）
        actor.addState(STATE_INSERTED_SINGLE);

        // 挿入カットイン
        $gameTemp.reserveCommonEvent(EVENT_INSERT_VAGINAL);

        // シルシェの反応
        if (!!$gameTemp.logWindowController) {
          $gameTemp.logWindowController.addLog(RES_REACTION_VAGINAL, null, '戦闘ログ');
        }

        // 一応変数系も更新しておく
        v.setValue(VARIABLE_CURRENT_INSERTED, 1);
        remainHole = Math.min(2, maxHoles - 1);
      }

      // 残りのシルシェ穴を更新
      actor.setStateLevel(STATE_BLANC_HOLE, remainHole);
      v.setValue(VARIABLE_INSERTABLE, remainHole);

      // 成功時のメッセージ
      if (BattleManager && BattleManager._logWindow) {
        BattleManager._logWindow.addText(TextResource.getText(RES_SUCCESS_INSERT));
      }
    } else {
      // シルシェの穴が塞がってるから挿入できません
      if (BattleManager && BattleManager._logWindow) {
        BattleManager._logWindow.addText(TextResource.getText(RES_FAILED_INSERT));
      }
    }
  }

  // アクション成功フラグを立てておく（失敗メッセージ抑止）
  this.makeSuccess(actor);

  // ここに来た時点で挿入したかできなかったかなのでもう挿入を試みないようにスイッチ更新
  s.setValue(SWITCH_INSERTABLE, remainHole > 0);

  return 0; // ダメージ式併用用
};

/**
 * おちんちん抜く
 */
Game_Action.prototype.unplugDick = function(b) {
  const buttler = this.subject();
  const actor = $gameActors.actor(1); // シルシェ
  const blackHoles = actor.stateLevel(STATE_BLANC_HOLE);
  const maxHoles = $gameVariables.value(VARIABLE_MAX_INSERTABLE);

  if (blackHoles === maxHoles) {
    // 無防備な淫穴が最大値。と言うことは抜くチンポがないのでなにもしない。
    return;
  }

  const single = actor.stateLevel(STATE_INSERTED_SINGLE);
  const double = actor.stateLevel(STATE_INSERTED_DOUBLE);
  const triple = actor.stateLevel(STATE_INSERTED_TRIPLE);

  actor.addState(STATE_BLANC_HOLE);  // 無防備な淫穴を一つ戻す
  if (triple > 0) {    
    // 三つ穴
    actor.removeState(STATE_INSERTED_TRIPLE);
    actor.addState(STATE_INSERTED_DOUBLE);

    // 変数系も更新
    const remain = Math.min((maxHoles - 2), 1);
    $gameVariables.setValue(VARIABLE_INSERTABLE, remain);
    $gameVariables.setValue(VARIABLE_CURRENT_INSERTED, 2);

    // 淫穴スイッチを残り穴があるかないかで更新
    $gameSwitches.setValue(SWITCH_INSERTABLE, (remain > 0));
  } else if (double > 0) {
    // 二ツ穴
    actor.removeState(STATE_INSERTED_DOUBLE);
    actor.addState(STATE_INSERTED_SINGLE);

    // 変数系も更新
    const remain = Math.min((maxHoles - 1), 2);
    $gameVariables.setValue(VARIABLE_INSERTABLE, remain);
    $gameVariables.setValue(VARIABLE_CURRENT_INSERTED, 1);

    // 淫穴スイッチを残り穴があるかないかで更新
    $gameSwitches.setValue(SWITCH_INSERTABLE, (remain > 0));
  } else if (single > 0) {
    // 一つ穴
    actor.removeState(STATE_INSERTED_SINGLE);

    // 変数系も更新
    const remain = Math.min(maxHoles, 3);
    $gameVariables.setValue(VARIABLE_INSERTABLE, remain);
    $gameVariables.setValue(VARIABLE_CURRENT_INSERTED, 0);

    // 淫穴スイッチを残り穴があるかないかで更新
    $gameSwitches.setValue(SWITCH_INSERTABLE, (remain > 0));
  } else {
    // ここに入ってくることはあり得ない。バグってる。
  }

  // 敵の挿入ステート解除
  if (!!buttler) {
    buttler.removeState(STATE_ENEMY_INSERT);
  }
  return 0;
};

/*
 * 挿入許可
 */
Game_Temp.prototype.allowSex = function() {
  const actor = $gameActors.actor(1);     // シルシェ
  const blancHoles = actor.stateLevel(STATE_BLANC_HOLE);
  if (blancHoles > 0) {
    // もう既に挿入可能な状態なのでなにもしない
    return;
  }

  const single = actor.stateLevel(STATE_INSERTED_SINGLE);
  const double = actor.stateLevel(STATE_INSERTED_DOUBLE);
  const triple = actor.stateLevel(STATE_INSERTED_TRIPLE);
  if (single > 0 || double > 0 || triple > 0) {
    // もう既に挿入可能かつ全穴挿入済みなので何もしない
    return;
  }

  const maxHole = $gameVariables.value(VARIABLE_MAX_INSERTABLE);

  // ステートをスタック
  actor.setStateLevel(10, maxHole);

  // 挿入可能変数も更新
  $gameVariables.setValue(VARIABLE_INSERTABLE, maxHole);

  // 挿入可能になったので敵の行動制御スイッチをON
  $gameSwitches.setValue(SWITCH_INSERTABLE, true);
};

const STATE_BIND = 33;                    // ステートID：拘束
const STATE_INCAPABLE = 37;               // ステートID：行動不能
const STATE_NON_RESISTANCE = 38;          // ステートID：無抵抗状態
const STATE_SLABELY = 42;                 // ステートID：純潔奉仕の姿勢
const RES_OUT_PENIS = "0900-065-n065-0";  // チンポが抜けた

/*
 * 挿入禁止
 */
Game_Temp.prototype.denySexInternal = function(withPull) {
  const actor = $gameActors.actor(1);     // シルシェ

  const bindLevel = actor.stateLevel(STATE_BIND);
  const incapableLevel = actor.stateLevel(STATE_INCAPABLE);
  const nonResistanceLevel = actor.stateLevel(STATE_NON_RESISTANCE);
  const slavelyLevel = actor.stateLevel(STATE_SLABELY);

  if ((bindLevel + incapableLevel + nonResistanceLevel + slavelyLevel) > 0) {
      // なんらかの挿入可能ステートが解除されたが、
      // まだ挿入可能にするステートが残っているので解除はしない
      return;
  }

  if (withPull === true) {
    // 挿入中ならば抜く
    $gameTroop.members().forEach(enemy => {
        const inserted = enemy.stateLevel(STATE_ENEMY_INSERT);
        if (inserted > 0) {
          enemy.removeState(STATE_ENEMY_INSERT);
          if (BattleManager && BattleManager._logWindow) {
            const name = enemy.name();
            BattleManager._logWindow.addText(TextResource.format(RES_OUT_PENIS, name));
          }
        }
    });

    // 変数系を更新
    $gameVariables.setValue(VARIABLE_CURRENT_INSERTED, 0);
    $gameVariables.setValue(VARIABLE_INSERTABLE, 0);

    // 挿入中のステートを解除
    actor.removeState(STATE_INSERTED_SINGLE);
    actor.removeState(STATE_INSERTED_DOUBLE);
    actor.removeState(STATE_INSERTED_TRIPLE);
  }

  // もう挿入できないので敵の行動制御スイッチもOFF
  $gameSwitches.setValue(SWITCH_INSERTABLE, false);

  // 無防備な淫穴も解除
  actor.eraseState(STATE_BLANC_HOLE);
}

/*
 * 挿入禁止
 */
Game_Temp.prototype.denySex = function() {
  this.denySexInternal(true);
};


const STATE_HEAVY_DEDICATION = 207;           // ステートID：重度献上マゾ
const RES_HDM_FIRE = "0900-070-n070-0";       // リソースラベル：「%1の効果で%2が全回復した！」

/**
 * 重度献上マゾ回復処理
 */
Game_Actor.prototype.reviveByHeavyDedication = function() {
  const log = BattleManager._logWindow;

  this._hp = this.mhp; // HP全回復
  this._mp = this.mmp; // MP全回復

  // 回復SE
  AudioManager.playSe({
    "name": "Heal3",
    "volume": 90,
    "pitch": 100,
    "pan": 0
  });

  // アナウンス
  const stateName = $dataStates[STATE_HEAVY_DEDICATION].name;
  log.push("addText", TextResource.format(RES_HDM_FIRE, stateName, this.name()));
  
  // 念のため重度献上マゾ発動フラグをOFFに倒しておく
  $gameSwitches.setValue(SWITCH_SUCTION_FAILED, false);

  // 重度献上マゾ削除
  this.removeState(STATE_HEAVY_DEDICATION);
  this.refresh();
};

// 戦闘で死にそうになったときの重度献上マゾ発動チェック
const _Game_Actor_prototype_refresh = Game_Actor.prototype.refresh;
Game_Actor.prototype.refresh = function() {
    if (this._hp <= 0 && this.stateLevel(STATE_HEAVY_DEDICATION) > 0) {
      // 発動条件を満たしているので復活
      this.reviveByHeavyDedication();
    } else {
      _Game_Actor_prototype_refresh.apply(this, arguments);
    }
};

/**
 * 吸淫処理
 * 
 * 
 */
const STATE_IMMORTAL = 3;                     // ステートID：不死身
const SWITCH_ALREADY_IMMORTAL = 802;          // スイッチID：吸淫時不死身判定（廃止予定）
const SWITCH_SUCTION_FAILED = 804;            // スイッチID：吸淫失敗フラグ（廃止予定）
const SWITCH_HDM_TRIGGER = 805;               // スイッチID：重度献上マゾ発動フラグ（廃止予定）
const VARIABLE_BATTLER_TP = 894;              // 変数ID：吸淫時看守TP（撤退判定に使ってたが廃止予定）
const VARIABLE_SUCTION_COUNT = 40;            // 変数ID：戦闘中吸淫回数
const VARIABLE_PROSTITUTE_LEVEL = 123;        // 変数ID：シルシェ娼婦レベル
const EVENT_LEVEL_DOWN = 1325;                // コモンイベント：レベルダウン処理
const EVENT_LABORED_COMMENT = 1333;           // コモンイベント：ご奉仕成功コメント
const EVENT_BATTLE_WIN = 1582;                // コモンイベント：吸淫用ご奉仕成功カットイン
const EVENT_DOGEZA_ANNOUNCE = 1072;           // コモンイベント：吸淫土下座アナウンス
const RES_SUCTION = "0900-035-n035-0";        // リソースラベル：「シルシェのレベルが邪悪の糧となる…！」
const RES_LOST_ENEMY_TP = "0900-036-n036-0";  // リソースラベル：「%1の発情値が%2減少した」
const RES_LEAVE_BATTLE = "0900-037-n037-0";   // リソースラベル：「%1は卑しく訓練された囚人の奉仕に満足して立ち去った」
const RES_SUCTION_FAILED = "0900-069-n069-0"; // リソースラベル：「%1は吸淫できなかった！」
const RES_UNSATISFIED = "0900-071-n071-0";    // リソースラベル：「%1はご奉仕にまだ満足していない……」

const ELEMENT_TP_LOSS = 6;                    // 属性：TP減少
const ELEMENT_SUCTION = 11;                   // 属性：吸淫

Game_Action.prototype.suction = function (b) {
  const buttler = this.subject();
  const actor = $gameActors.actor(1); // シルシェ
  const log = BattleManager._logWindow;

  const alreadyImmortal = actor.stateLevel(STATE_IMMORTAL) > 0; // シルシェが既に不死身か
  if (alreadyImmortal) {
    // この後敵が撤退しても戦闘終了にならないように

    // 一応スイッチを立てておく
    $gameSwitches.setValue(SWITCH_ALREADY_IMMORTAL, alreadyImmortal);
  }

  // アクション成功フラグを立てておく（失敗メッセージ抑止）
  this.makeSuccess(actor);

  if (actor.tp >= 100) {
    // シルシェの発情値がMAX

    // 一応吸淫失敗フラグは倒しておく
    $gameSwitches.setValue(SWITCH_SUCTION_FAILED, false);

    log.push("addText", TextResource.getText(RES_SUCTION));

    // 一応敵のTPを待避しておく
    $gameVariables.setValue(VARIABLE_BATTLER_TP, buttler.tp);

    // レベルダウン
    $gameTemp.reserveCommonEvent(EVENT_LEVEL_DOWN);

    // ご奉仕成功コメント
    $gameTemp.reserveCommonEvent(EVENT_LABORED_COMMENT);

    // 発情値減少コメントは冗長なのでカット
    // log.push("addText", TextResource.format(RES_LOST_ENEMY_TP, buttler.name(), 100));

    const remainEnemies = $gameTroop.aliveMembers().length;

    // どっちにしろコイツは吸淫したので撤退
    if (buttler.tp >= 100) {
      this.unplugDick(0); // チンポを抜くのを忘れずに
      BattleManager._logWindow.push("addText", TextResource.format(RES_LEAVE_BATTLE, buttler.name()));
      $gameVariables.setValue(VARIABLE_SUCTION_COUNT, $gameVariables.value(VARIABLE_SUCTION_COUNT) + 1);
      buttler.escape();
    } else {
      // すっきりしたのでせっかくの看守の発情値も減少
      const prostituteLevel = $gameVariables.value(VARIABLE_PROSTITUTE_LEVEL);
      // 属性有効度に応じて8 ～ 20位 * シルシェの娼婦レベル％（最低1）
      const lossBattlerTp = Math.floor(20 * actor.elementRate(ELEMENT_SUCTION) * (prostituteLevel / 100));
      buttler.gainTp(Math.max(lossBattlerTp, 1) * -1);
      BattleManager._logWindow.push("addText", TextResource.format(RES_UNSATISFIED, buttler.name()));

      // 敵が撤退しないので無限吸淫編を避けるためのアナウンス
      $gameTemp.reserveCommonEvent(EVENT_DOGEZA_ANNOUNCE);
    }

    // 与ダメージの計算（シルシェの最大HPｘ吸淫属性有効度）
    const rawDamage = actor.mhp * actor.elementRate(ELEMENT_SUCTION);

    // 敵が残り1体かつ撤退するか？（＝全員いなくなってシルシェ勝利になるか）
    if (remainEnemies === 1 && buttler.tp >= 100) {
      log.clear();

      // 最後の一人（これで全員撤退＝シルシェ勝利）

      // シルシェの発情値減少
      const tpLoss = Math.floor((60 + Math.randomInt(21)) * actor.elementRate(ELEMENT_TP_LOSS));
      actor.gainTp(-tpLoss);

      // 戦闘終了時イベントのセット
      $gameTemp.reserveCommonEvent(EVENT_BATTLE_WIN);

      if (actor.hp <= rawDamage) {
        // このままだとシルシェのHPが0になって敗北する

        // 重度献上マゾがある場合重度献上マゾの発動条件を満たすのでフラグセットする
        $gameSwitches.setValue(SWITCH_SUCTION_FAILED, actor.stateLevel(STATE_HEAVY_DEDICATION) > 0);
        return actor.hp - 1; // HP1残しする
      } else {
        return rawDamage; // 通常のダメージ計算
      }
    }

    return rawDamage;
  } else {
    // 吸淫できないのでミス
    $gameSwitches.setValue(SWITCH_SUCTION_FAILED, true);
    log.push("addText", TextResource.format(RES_SUCTION_FAILED, buttler.name()));
    return 0;
  }
};
 
  /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   * ▲ END OF FORMULAS ▲
   * ここより下は触らない
   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
})();
