/*:ja
 * @target MZ
 * @plugindesc アクターウィンドウのレイアウトとHPゲージの機能を組み合わせ、落ちものパズル風の演出を追加します。フロントビュー対応機能も含みます。
 * @author えむうん
 * @base NUUN_Base
 * @orderAfter NUUN_Base
 * @orderAfter NUUN_BattleStyleEX
 *
 * @param WindowWidth
 * @text ウィンドウの幅
 * @desc アクターウィンドウの幅
 * @default 280
 * @type number
 *
 * @param WindowHeight
 * @text ウィンドウの高さ
 * @desc アクターウィンドウの高さ
 * @default 180
 * @type number
 *
 * @param WindowSpacing
 * @text ウィンドウの間隔
 * @desc アクターウィンドウ間の間隔
 * @default 10
 * @type number
 *
 * @param SlideSpeed
 * @text スライド速度
 * @desc ウィンドウのスライド速度
 * @default 4
 * @type number
 *
 * @param FallingSpeed
 * @text 落下速度
 * @desc ウィンドウの落下速度
 * @default 8
 * @type number
 *
 * @param RisingSpeed
 * @text 上昇速度
 * @desc ウィンドウの上昇速度
 * @default 12
 * @type number
 *
 * @param CommandWindowX
 * @text コマンドウィンドウのX座標
 * @desc コマンドウィンドウのX座標
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param CommandWindowY
 * @text コマンドウィンドウのY座標
 * @desc コマンドウィンドウのY座標
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param ActorCommandWindowX
 * @text アクターコマンドウィンドウのX座標
 * @desc アクターコマンドウィンドウのX座標
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param ActorCommandWindowY
 * @text アクターコマンドウィンドウのY座標
 * @desc アクターコマンドウィンドウのY座標
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param WindowOpacity
 * @text ウィンドウの不透明度
 * @desc アクターウィンドウの不透明度（0-255）
 * @default 255
 * @type number
 * @min 0
 * @max 255
 *
 * @param FaceX
 * @text 顔グラフィックのX座標
 * @desc 顔グラフィックのX座標
 * @default 0
 * @type number
 *
 * @param FaceY
 * @text 顔グラフィックのY座標
 * @desc 顔グラフィックのY座標
 * @default 0
 * @type number
 *
 * @param FaceWidth
 * @text 顔グラフィックの幅
 * @desc 顔グラフィックの幅
 * @default 120
 * @type number
 *
 * @param FaceHeight
 * @text 顔グラフィックの高さ
 * @desc 顔グラフィックの高さ
 * @default 120
 * @type number
 *
 * @param NameX
 * @text 名前のX座標
 * @desc 名前のX座標
 * @default 130
 * @type number
 *
 * @param NameY
 * @text 名前のY座標
 * @desc 名前のY座標
 * @default 0
 * @type number
 *
 * @param HPGaugeX
 * @text HPゲージのX座標
 * @desc HPゲージのX座標
 * @default 130
 * @type number
 *
 * @param HPGaugeY
 * @text HPゲージのY座標
 * @desc HPゲージのY座標
 * @default 30
 * @type number
 *
 * @param MPGaugeX
 * @text MPゲージのX座標
 * @desc MPゲージのX座標
 * @default 130
 * @type number
 *
 * @param MPGaugeY
 * @text MPゲージのY座標
 * @desc MPゲージのY座標
 * @default 60
 * @type number
 *
 * @param TPGaugeX
 * @text TPゲージのX座標
 * @desc TPゲージのX座標
 * @default 130
 * @type number
 *
 * @param TPGaugeY
 * @text TPゲージのY座標
 * @desc TPゲージのY座標
 * @default 90
 * @type number
 *
 * @param StateIconX
 * @text ステートアイコンのX座標
 * @desc ステートアイコンのX座標
 * @default 130
 * @type number
 *
 * @param StateIconY
 * @text ステートアイコンのY座標
 * @desc ステートアイコンのY座標
 * @default 120
 * @type number
 *
 * @param TPBGaugeX
 * @text タイムプログレスバーのX座標
 * @desc タイムプログレスバーのX座標
 * @default 130
 * @type number
 *
 * @param TPBGaugeY
 * @text タイムプログレスバーのY座標
 * @desc タイムプログレスバーのY座標
 * @default 150
 * @type number
 *
 * @param HPGaugeImageFull
 * @text HPゲージ満タン時の画像
 * @desc HPゲージ満タン時の画像ファイル名
 * @default HPGaugeFull
 * @dir img/system/
 * @type file
 *
 * @param HPGaugeImageEmpty
 * @text HPゲージ空の時の画像
 * @desc HPゲージ空の時の画像ファイル名
 * @default HPGaugeEmpty
 * @dir img/system/
 * @type file
 *
 * @param GaugeDirection
 * @text ゲージの塗りつぶし方向
 * @desc HPゲージが塗りつぶされる方向
 * @type select
 * @option 左から右
 * @value right
 * @option 右から左
 * @value left
 * @option 下から上
 * @value up
 * @option 上から下
 * @value down
 * @default right
 *
 * @param GaugeHeight
 * @text ゲージの高さ
 * @desc ゲージの高さ
 * @default 12
 * @type number
 *
 * @param FontSize
 * @text フォントサイズ
 * @desc ステータス数値のフォントサイズ
 * @default 18
 * @type number
 *
 * @param HPValueX
 * @text HP数値のX座標
 * @desc HP数値のX座標（相対位置）
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param HPValueY
 * @text HP数値のY座標
 * @desc HP数値のY座標（相対位置）
 * @default 0
 * @type number
 * @min -9999
 * @max 9999
 *
 * @param HideWindowsSwitchId
 * @text ウィンドウ非表示スイッチID
 * @desc このスイッチがONの時、アクターウィンドウを非表示にします
 * @default 1
 * @type switch
 *
 * @param ShowTPGauge
 * @text TPゲージを表示
 * @desc TPゲージを表示するかどうか
 * @type boolean
 * @on 表示する
 * @off 表示しない
 * @default true
 *
 * @help
 * このプラグインは、CustomActorWindowLayoutとCustomHPGaugeプラグインの
 * 機能を組み合わせたものです。戦闘中のアクターウィンドウを垂直レイアウトに
 * 変更し、HPゲージにカスタム画像を使用します。
 * 
 * 機能：
 * - アクティブなアクターのウィンドウのみが光るようになりました。
 * - 戦闘不能になったアクターは右に移動し、画面外に出て見えなくなります。
 * - HPゲージが各アクター独立して動作するように修正されました。
 * - タイムプログレスバーの値が大きいキャラクターほど下に配置されます。
 * - 一番下のキャラクターが行動した後、そのウィンドウが一時的に消え、
 *   上から降ってきて通常状態に戻ります。
 * - ゲージサイズの調整とフォントサイズの調整機能を追加しました。
 * - ステータス数値をゲージに重なるように表示するようになりました。
 * - HPのステータス数値の位置を調整できるようになりました。
 * - プログレスバーの値が満タンになったとき、一番下のアクターを優先して
 *   選択し、コマンドを表示するようになりました。
 * - アクターウィンドウが常時表示されるようになりました。
 * - 指定したスイッチでアクターウィンドウの表示/非表示を切り替えられます。
 * - TPゲージの表示/非表示をプラグインパラメータで設定できます。
 */

(() => {
    'use strict';
    const pluginName = "CombinedCustomActorHPGauge";
    const parameters = PluginManager.parameters(pluginName);
    const WindowWidth = Number(parameters['WindowWidth'] || 280);
    const WindowHeight = Number(parameters['WindowHeight'] || 180);
    const WindowSpacing = Number(parameters['WindowSpacing'] || 10);
    const SlideSpeed = Number(parameters['SlideSpeed'] || 4);
    const FallingSpeed = Number(parameters['FallingSpeed'] || 8);
    const RisingSpeed = Number(parameters['RisingSpeed'] || 12);
    const CommandWindowX = Number(parameters['CommandWindowX'] || 0);
    const CommandWindowY = Number(parameters['CommandWindowY'] || 0);
    const ActorCommandWindowX = Number(parameters['ActorCommandWindowX'] || 0);
    const ActorCommandWindowY = Number(parameters['ActorCommandWindowY'] || 0);
    const WindowOpacity = Number(parameters['WindowOpacity'] || 255);
    const FaceX = Number(parameters['FaceX'] || 0);
    const FaceY = Number(parameters['FaceY'] || 0);
    const FaceWidth = Number(parameters['FaceWidth'] || 120);
    const FaceHeight = Number(parameters['FaceHeight'] || 120);
    const NameX = Number(parameters['NameX'] || 130);
    const NameY = Number(parameters['NameY'] || 0);
    const HPGaugeX = Number(parameters['HPGaugeX'] || 130);
    const HPGaugeY = Number(parameters['HPGaugeY'] || 30);
    const MPGaugeX = Number(parameters['MPGaugeX'] || 130);
    const MPGaugeY = Number(parameters['MPGaugeY'] || 60);
    const TPGaugeX = Number(parameters['TPGaugeX'] || 130);
    const TPGaugeY = Number(parameters['TPGaugeY'] || 90);
    const StateIconX = Number(parameters['StateIconX'] || 130);
    const StateIconY = Number(parameters['StateIconY'] || 120);
    const TPBGaugeX = Number(parameters['TPBGaugeX'] || 130);
    const TPBGaugeY = Number(parameters['TPBGaugeY'] || 150);
    const HPGaugeImageFull = parameters['HPGaugeImageFull'] || 'HPGaugeFull';
    const HPGaugeImageEmpty = parameters['HPGaugeImageEmpty'] || 'HPGaugeEmpty';
    const GaugeDirection = parameters['GaugeDirection'] || 'right';
    const GaugeHeight = Number(parameters['GaugeHeight'] || 12);
    const FontSize = Number(parameters['FontSize'] || 18);
    const HPValueX = Number(parameters['HPValueX'] || 0);
    const HPValueY = Number(parameters['HPValueY'] || 0);
    const HideWindowsSwitchId = Number(parameters['HideWindowsSwitchId'] || 1);
    const ShowTPGauge = JSON.parse(parameters['ShowTPGauge'] || 'true');

    //-----------------------------------------------------------------------------
    // Scene_Battle
    //

    const _Scene_Battle_createStatusWindow = Scene_Battle.prototype.createStatusWindow;
    Scene_Battle.prototype.createStatusWindow = function() {
        _Scene_Battle_createStatusWindow.call(this);
        if (this._statusWindow instanceof Window_BattleStatus) {
            this._statusWindow.x = Graphics.boxWidth - WindowWidth;
            this._statusWindow.y = 0;
            this._statusWindow.width = WindowWidth;
            this._statusWindow.height = Graphics.boxHeight;
            this._statusWindow.opacity = WindowOpacity;
        }
        this.setStatusWindowForBattleManager();
    };

    Scene_Battle.prototype.setStatusWindowForBattleManager = function() {
        BattleManager.setStatusWindow(this._statusWindow);
    };

    const _Scene_Battle_updateStatusWindowPosition = Scene_Battle.prototype.updateStatusWindowPosition;
    Scene_Battle.prototype.updateStatusWindowPosition = function() {
        // Do nothing to keep the status window in place
    };

    const _Scene_Battle_startActorCommandSelection = Scene_Battle.prototype.startActorCommandSelection;
    Scene_Battle.prototype.startActorCommandSelection = function() {
        _Scene_Battle_startActorCommandSelection.call(this);
        if (this._statusWindow) {
            this._statusWindow.x = Graphics.boxWidth - WindowWidth;
        }
    };

    const _Scene_Battle_partyCommandWindowRect = Scene_Battle.prototype.partyCommandWindowRect;
    Scene_Battle.prototype.partyCommandWindowRect = function() {
        const rect = _Scene_Battle_partyCommandWindowRect.call(this);
        rect.x += CommandWindowX;
        rect.y += CommandWindowY;
        return rect;
    };

    const _Scene_Battle_actorCommandWindowRect = Scene_Battle.prototype.actorCommandWindowRect;
    Scene_Battle.prototype.actorCommandWindowRect = function() {
        const rect = _Scene_Battle_actorCommandWindowRect.call(this);
        rect.x += ActorCommandWindowX;
        rect.y += ActorCommandWindowY;
        return rect;
    };

    //-----------------------------------------------------------------------------
    // Window_BattleStatus
    //

    const _Window_BattleStatus_initialize = Window_BattleStatus.prototype.initialize;
    Window_BattleStatus.prototype.initialize = function(rect) {
        _Window_BattleStatus_initialize.call(this, rect);
        this._actorWindows = [];
        this.createActorWindows();
    };

    Window_BattleStatus.prototype.createActorWindows = function() {
        const maxBattleMembers = $gameParty.maxBattleMembers();
        for (let i = 0; i < maxBattleMembers; i++) {
            const actorWindow = new Window_CustomBattleActor(new Rectangle(0, 0, WindowWidth, WindowHeight));
            actorWindow.padding = this.padding;
            actorWindow.backOpacity = this.backOpacity;
            actorWindow.opacity = WindowOpacity;
            this._actorWindows.push(actorWindow);
            this.addInnerChild(actorWindow);
        }
    };

    const _Window_BattleStatus_update = Window_BattleStatus.prototype.update;
    Window_BattleStatus.prototype.update = function() {
        _Window_BattleStatus_update.call(this);
        this.updateActorWindows();
    };

    Window_BattleStatus.prototype.updateActorWindows = function() {
        const maxBattleMembers = $gameParty.maxBattleMembers();
        const battleMembers = $gameParty.battleMembers();
        const aliveBattleMembers = $gameParty.aliveBattleMembers();
        
        // Sort alive members by their TPB gauge value (descending order)
        aliveBattleMembers.sort((a, b) => b.tpbChargeTime() - a.tpbChargeTime());

        let index = 0;
        for (let i = 0; i < maxBattleMembers; i++) {
            const actorWindow = this._actorWindows[i];
            const actor = battleMembers[i];
            
            if (actor) {
                actorWindow.show();
                actorWindow.setBattler(actor);
                actorWindow.setActive(BattleManager.actor() === actor);
                
                if (actor.isAlive()) {
                    const aliveIndex = aliveBattleMembers.indexOf(actor);
                    const targetY = this.height - (aliveIndex + 1) * (WindowHeight + WindowSpacing);
                    const targetX = 0;
                    actorWindow.updatePosition(targetX, targetY);
                    index++;
                } else {
                    const targetX = Graphics.width; // Move dead actors completely off-screen
                    actorWindow.updatePosition(targetX, actorWindow.y);
                }
            } else {
                actorWindow.hide();
            }
        }
    };

    Window_BattleStatus.prototype.drawAllItems = function() {
        // Override the original drawing method (do nothing)
    };

    Window_BattleStatus.prototype.drawItem = function(index) {
        // Override the original drawing method (do nothing)
    };

    Window_BattleStatus.prototype.refreshCursor = function() {
        if (this._cursorAll) {
            this.refreshCursorForAll();
        } else if (this.index() >= 0) {
            const actorWindow = this._actorWindows[this.index()];
            if (actorWindow) {
                actorWindow.refreshCursor();
            }
        } else {
            this.setCursorRect(0, 0, 0, 0);
        }
    };

    Window_BattleStatus.prototype.refreshCursorForAll = function() {
        this._actorWindows.forEach(actorWindow => {
            actorWindow.setCursorRect(0, 0, actorWindow.width, actorWindow.height);
        });
    };

    Window_BattleStatus.prototype.updateVisibility = function() {
        const visible = !$gameSwitches.value(HideWindowsSwitchId);
        this.visible = visible;
        this._actorWindows.forEach(window => window.visible = visible);
    };

    //-----------------------------------------------------------------------------
    // Window_CustomBattleActor
    //

    function Window_CustomBattleActor() {
        this.initialize(...arguments);
    }

    Window_CustomBattleActor.prototype = Object.create(Window_StatusBase.prototype);
    Window_CustomBattleActor.prototype.constructor = Window_CustomBattleActor;

    Window_CustomBattleActor.prototype.initialize = function(rect) {
        Window_StatusBase.prototype.initialize.call(this, rect);
        this._actor = null;
        this._tpbGaugeSprite = null;
        this._gauges = {};
        this.createTPBGauge();
        this._isActive = false;
        this._targetX = 0;
        this._targetY = 0;
        this._state = 'idle'; // 'idle', 'falling', 'rising', 'hiding', 'sliding'
        this._damageSprites = [];
        this._effectsContainer = new Sprite();
        this.addChild(this._effectsContainer);
        this.refresh();
    };

    Window_CustomBattleActor.prototype.createTPBGauge = function() {
        this._tpbGaugeSprite = new Sprite_Gauge();
        this.addInnerChild(this._tpbGaugeSprite);
    };

    Window_CustomBattleActor.prototype.setBattler = function(battler) {
        if (this._actor !== battler) {
            this._actor = battler;
            this.refresh();
        }
    };

    Window_CustomBattleActor.prototype.refresh = function() {
        this.contents.clear();
        if (this._actor) {
            this.drawActorFace(this._actor, FaceX, FaceY, FaceWidth, FaceHeight);
            this.drawActorName(this._actor, NameX, NameY, this.width - this.padding * 2);
            this.placeBasicGauges(this._actor);
            this.drawActorIcons(this._actor, StateIconX, StateIconY);
            if (BattleManager.isTpb()) {
                this.placeTPBGauge(this._actor);
            }
        }
    };

    Window_CustomBattleActor.prototype.drawActorFace = function(actor, x, y, width, height) {
        this.drawFace(actor.faceName(), actor.faceIndex(), x, y, width, height);
    };

    Window_CustomBattleActor.prototype.drawActorName = function(actor, x, y, width) {
        width = width || 168;
        this.contents.fontSize = FontSize;
        this.changeTextColor(ColorManager.hpColor(actor));
        this.drawText(actor.name(), x, y, width);
    };

    Window_CustomBattleActor.prototype.placeBasicGauges = function(actor) {
        this.placeGauge(actor, "hp", HPGaugeX, HPGaugeY);
        this.placeGauge(actor, "mp", MPGaugeX, MPGaugeY);
        if (ShowTPGauge && $dataSystem.optDisplayTp) {
            this.placeGauge(actor, "tp", TPGaugeX, TPGaugeY);
        }
    };

    Window_CustomBattleActor.prototype.drawActorIcons = function(actor, x, y) {
        const icons = actor.allIcons().slice(0, Math.floor((this.width - this.padding * 2) / ImageManager.iconWidth));
        let iconX = x;
        for (const icon of icons) {
            this.drawIcon(icon, iconX, y);
            iconX += ImageManager.iconWidth;
        }
    };

    Window_CustomBattleActor.prototype.placeTPBGauge = function(actor) {
        if (BattleManager.isTpb() && this._tpbGaugeSprite) {
            this._tpbGaugeSprite.setup(actor, "time");
            this._tpbGaugeSprite.move(TPBGaugeX, TPBGaugeY);
            this._tpbGaugeSprite.show();
        } else if (this._tpbGaugeSprite) {
            this._tpbGaugeSprite.hide();
        }
    };

    Window_CustomBattleActor.prototype.refreshCursor = function() {
        this.setCursorRect(0, 0, this.width, this.height);
    };

    Window_CustomBattleActor.prototype.setActive = function(active) {
        if (this._isActive !== active) {
            this._isActive = active;
            this.refreshCursor();
        }
    };

    Window_CustomBattleActor.prototype._updateCursor = function() {
        if (this._isActive) {
            Window_Selectable.prototype._updateCursor.call(this);
        } else {
            this.setCursorRect(0, 0, 0, 0);
        }
    };

    const _Window_CustomBattleActor_update = Window_CustomBattleActor.prototype.update;
    Window_CustomBattleActor.prototype.update = function() {
        _Window_CustomBattleActor_update.call(this);
        if (this._tpbGaugeSprite) {
            this._tpbGaugeSprite.update();
        }
        this.updateGauges();
        this.updateAnimation();
        this.updateDamagePopup();
        if (this._actor && !this._actor.isAlive() && this.x < Graphics.width) {
            this.x += 10; // Adjust this value to change the speed of movement
        }
        if (this._actor && this._actor._statusRefreshRequested) {
            this.refreshStatus();
            this._actor._statusRefreshRequested = false;
        }
    };

    Window_CustomBattleActor.prototype.updateGauges = function() {
        if (this._actor) {
            for (const type in this._gauges) {
                if (type !== "tp" || (ShowTPGauge && $dataSystem.optDisplayTp)) {
                    this._gauges[type].update();
                }
            }
        }
    };

    Window_CustomBattleActor.prototype.placeGauge = function(actor, type, x, y) {
        if (this._gauges[type]) {
            this._gauges[type].setup(actor, type);
            this._gauges[type].move(x, y);
            this._gauges[type].show();
        } else {
            const key = "actor%1-gauge-%2".format(actor.actorId(), type);
            const sprite = this.createInnerSprite(key, Sprite_CustomGauge);
            sprite.setup(actor, type);
            sprite.move(x, y);
            sprite.show();
            this._gauges[type] = sprite;
        }
    };

    Window_CustomBattleActor.prototype.updatePosition = function(targetX, targetY) {
        this._targetX = targetX;
        this._targetY = targetY;
        if (this._state === 'idle') {
            this._state = 'falling';
        }
    };

    Window_CustomBattleActor.prototype.updateAnimation = function() {
        switch (this._state) {
            case 'falling':
                this.y += FallingSpeed;
                if (this.y >= this._targetY) {
                    this.y = this._targetY;
                    this._state = 'idle';
                }
                break;
            case 'rising':
                this.y -= RisingSpeed;
                if (this.y <= this._targetY) {
                    this.y = this._targetY;
                    this._state = 'idle';
                }
                this.opacity += 25;
                if (this.opacity >= 255) {
                    this.opacity = 255;
                }
                break;
            case 'hiding':
                this.opacity -= 25;
                if (this.opacity <= 0) {
                    this.opacity = 0;
                    this.y = -WindowHeight;
                    this._state = 'falling';
                }
                break;
            case 'sliding':
                this.x += (this._targetX - this.x) / SlideSpeed;
                if (Math.abs(this._targetX - this.x) < 1) {
                    this.x = this._targetX;
                    this._state = 'idle';
                }
                break;
        }
    };

    Window_CustomBattleActor.prototype.startHidingAnimation = function() {
        if (this._state !== 'hiding') {
            this._state = 'hiding';
        }
    };

    Window_CustomBattleActor.prototype.updateDamagePopup = function() {
        if (this._actor && this._actor.isDamagePopupRequested()) {
            const sprite = new Sprite_Damage();
            sprite.x = this.width / 2 + HPValueX;
            sprite.y = this.height / 2 + HPValueY;
            sprite.setup(this._actor);
            this._effectsContainer.addChild(sprite);
            this._damageSprites.push(sprite);
            this._actor.clearDamagePopup();
        }
        this._damageSprites.forEach(sprite => {
            if (!sprite.isPlaying()) {
                this.removeDamageSprite(sprite);
            }
        });
    };

    Window_CustomBattleActor.prototype.removeDamageSprite = function(sprite) {
        this._damageSprites.remove(sprite);
        this._effectsContainer.removeChild(sprite);
        sprite.destroy();
    };

    Window_CustomBattleActor.prototype.refreshStatus = function() {
        if (this._gauges.mp) {
            this._gauges.mp.refresh();
        }
        this.refresh();
    };

    //-----------------------------------------------------------------------------
    // Sprite_CustomGauge
    //

    function Sprite_CustomGauge() {
        this.initialize(...arguments);
    }

    Sprite_CustomGauge.prototype = Object.create(Sprite_Gauge.prototype);
    Sprite_CustomGauge.prototype.constructor = Sprite_CustomGauge;

    Sprite_CustomGauge.prototype.initialize = function() {
        Sprite_Gauge.prototype.initialize.call(this);
        this._gaugeSpriteFull = new Sprite();
        this._gaugeSpriteEmpty = new Sprite();
        this.addChild(this._gaugeSpriteEmpty);
        this.addChild(this._gaugeSpriteFull);
        this._valueSprite = new Sprite();
        this.addChild(this._valueSprite);
        this._valueSprite.bitmap = new Bitmap(this.bitmapWidth(), this.bitmapHeight());
    };

    Sprite_CustomGauge.prototype.setup = function(battler, statusType) {
        Sprite_Gauge.prototype.setup.call(this, battler, statusType);
        if (statusType === 'hp' && battler.isActor()) {
            this._gaugeSpriteFull.bitmap = ImageManager.loadSystem(HPGaugeImageFull);
            this._gaugeSpriteEmpty.bitmap = ImageManager.loadSystem(HPGaugeImageEmpty);
        }
    };

    Sprite_CustomGauge.prototype.updateBitmap = function() {
        Sprite_Gauge.prototype.updateBitmap.call(this);
        if (this._statusType === 'hp' && this._battler.isActor()) {
            this._gaugeSpriteFull.bitmap = ImageManager.loadSystem(HPGaugeImageFull);
            this._gaugeSpriteEmpty.bitmap = ImageManager.loadSystem(HPGaugeImageEmpty);
        }
        this.redrawValue();
    };

    Sprite_CustomGauge.prototype.redrawValue = function() {
        const width = this.bitmapWidth();
        const height = this.bitmapHeight();
        if (!this._valueSprite.bitmap || this._valueSprite.bitmap.width !== width || this._valueSprite.bitmap.height !== height) {
            this._valueSprite.bitmap = new Bitmap(width, height);
        }
        this._valueSprite.bitmap.clear();
        this._valueSprite.bitmap.fontFace = $gameSystem.mainFontFace();
        this._valueSprite.bitmap.fontSize = FontSize;
        this._valueSprite.bitmap.textColor = this.valueColor();
        this._valueSprite.bitmap.outlineColor = this.valueOutlineColor();
        this._valueSprite.bitmap.outlineWidth = this.valueOutlineWidth();
        this.drawValue();
    };

    Sprite_CustomGauge.prototype.drawValue = function() {
        const currentValue = this.currentValue();
        const maxValue = this.currentMaxValue();
        const width = this.bitmapWidth();
        const height = this.bitmapHeight();
        this._valueSprite.bitmap.drawText(currentValue + "/" + maxValue, 0, 0, width, height, 'center');
        if (this._statusType === 'hp') {
            this._valueSprite.x = HPValueX;
            this._valueSprite.y = HPValueY;
        } else {
            this._valueSprite.x = 0;
            this._valueSprite.y = 0;
        }
    };

    Sprite_CustomGauge.prototype.drawGauge = function() {
        if (this._statusType === 'hp' && this._battler.isActor()) {
            const gaugeRect = this.gaugeRect();
            const rate = this.gaugeRate();
            
            this._gaugeSpriteEmpty.setFrame(0, 0, gaugeRect.width, gaugeRect.height);
            this._gaugeSpriteEmpty.x = gaugeRect.x;
            this._gaugeSpriteEmpty.y = gaugeRect.y;
            
            switch (GaugeDirection) {
                case 'right':
                    const fillW = Math.floor(gaugeRect.width * rate);
                    this._gaugeSpriteFull.setFrame(0, 0, fillW, gaugeRect.height);
                    this._gaugeSpriteFull.x = gaugeRect.x;
                    this._gaugeSpriteFull.y = gaugeRect.y;
                    break;
                case 'left':
                    const fillWLeft = Math.floor(gaugeRect.width * rate);
                    this._gaugeSpriteFull.setFrame(gaugeRect.width - fillWLeft, 0, fillWLeft, gaugeRect.height);
                    this._gaugeSpriteFull.x = gaugeRect.x + (gaugeRect.width - fillWLeft);
                    this._gaugeSpriteFull.y = gaugeRect.y;
                    break;
                case 'up':
                    const fillH = Math.floor(gaugeRect.height * rate);
                    this._gaugeSpriteFull.setFrame(0, gaugeRect.height - fillH, gaugeRect.width, fillH);
                    this._gaugeSpriteFull.x = gaugeRect.x;
                    this._gaugeSpriteFull.y = gaugeRect.y + (gaugeRect.height - fillH);
                    break;
                case 'down':
                    const fillHDown = Math.floor(gaugeRect.height * rate);
                    this._gaugeSpriteFull.setFrame(0, 0, gaugeRect.width, fillHDown);
                    this._gaugeSpriteFull.x = gaugeRect.x;
                    this._gaugeSpriteFull.y = gaugeRect.y;
                    break;
            }
        } else {
            Sprite_Gauge.prototype.drawGauge.call(this);
        }
    };

    Sprite_CustomGauge.prototype.gaugeRect = function() {
        const width = this.bitmapWidth();
        const height = this.bitmapHeight();
        return new Rectangle(0, 0, width, height);
    };

    Sprite_CustomGauge.prototype.bitmapWidth = function() {
        if (this._statusType === 'hp' && this._battler.isActor()) {
            return this._gaugeSpriteFull.bitmap ? this._gaugeSpriteFull.bitmap.width : 128;
        }
        return 128;
    };

    Sprite_CustomGauge.prototype.bitmapHeight = function() {
        if (this._statusType === 'hp' && this._battler.isActor()) {
            return this._gaugeSpriteFull.bitmap ? this._gaugeSpriteFull.bitmap.height : GaugeHeight;
        }
        return GaugeHeight;
    };

    Sprite_CustomGauge.prototype.refresh = function() {
        this.updateBitmap();
    };

    //-----------------------------------------------------------------------------
    // Game_Battler
    //

    const _Game_Battler_setMp = Game_Battler.prototype.setMp;
    Game_Battler.prototype.setMp = function(mp) {
        if (this._mp !== mp) {
            _Game_Battler_setMp.call(this, mp);
            this.requestStatusRefresh();
        }
    };

    Game_Battler.prototype.requestStatusRefresh = function() {
        this._statusRefreshRequested = true;
    };

    //-----------------------------------------------------------------------------
    // BattleManager
    //

    const _BattleManager_setup = BattleManager.setup;
    BattleManager.setup = function(troopId, canEscape, canLose) {
        _BattleManager_setup.call(this, troopId, canEscape, canLose);
        this._lastActionedActor = null;
        this._statusWindow = null;
    };

    BattleManager.setStatusWindow = function(statusWindow) {
        this._statusWindow = statusWindow;
    };

    const _BattleManager_startAction = BattleManager.startAction;
    BattleManager.startAction = function() {
        _BattleManager_startAction.call(this);
        if (this._subject && this._subject.isActor()) {
            this._lastActionedActor = this._subject;
        }
    };

    const _BattleManager_endAction = BattleManager.endAction;
    BattleManager.endAction = function() {
        _BattleManager_endAction.call(this);
        if (this._lastActionedActor) {
            const index = $gameParty.members().indexOf(this._lastActionedActor);
            if (index >= 0 && this._statusWindow && this._statusWindow._actorWindows) {
                const actorWindow = this._statusWindow._actorWindows[index];
                if (actorWindow.y === this._statusWindow.height - WindowHeight - WindowSpacing) {
                    actorWindow.startHidingAnimation();
                }
            }
            this._lastActionedActor = null;
        }
        if (this._statusWindow) {
            this._statusWindow.updateActorWindows();
        }
    };

    const _BattleManager_update = BattleManager.update;
    BattleManager.update = function(timeActive) {
        _BattleManager_update.call(this, timeActive);
        if (this._statusWindow) {
            this._statusWindow.updateActorWindows();
            this._statusWindow.updateVisibility();
        }
        this.updateTpbChargedActor();
    };

    BattleManager.updateTpbChargedActor = function() {
        if (this.isActiveTpb() && !this._subject) {
            const aliveBattleMembers = $gameParty.aliveBattleMembers();
            const tpbChargedActors = aliveBattleMembers.filter(actor => actor.isTimeProgressMax());
            if (tpbChargedActors.length > 0) {
                // Find the actor with the highest y-coordinate (lowest on screen)
                const lowestActor = tpbChargedActors.reduce((lowest, current) => {
                    const lowestIndex = aliveBattleMembers.indexOf(lowest);
                    const currentIndex = aliveBattleMembers.indexOf(current);
                    return currentIndex > lowestIndex ? current : lowest;
                });
                this._subject = lowestActor;
                this.startAction();
            }
        }
    };

    //-----------------------------------------------------------------------------
    // Game_Party
    //

    Game_Party.prototype.aliveBattleMembers = function() {
        return this.battleMembers().filter(actor => actor.isAlive());
    };

    //-----------------------------------------------------------------------------
    // Game_Actor
    //

    Game_Actor.prototype.tpbChargeTime = function() {
        return this._tpbChargeTime || 0;
    };

    //-----------------------------------------------------------------------------
    // Plugin Command Registration
    //

    PluginManager.registerCommand(pluginName, "refreshActorWindows", args => {
        if ($gameParty.inBattle()) {
            SceneManager._scene._statusWindow.refresh();
        }
    });

})();