/*=============================================================================
 ExtraGauge.js
----------------------------------------------------------------------------
 (C)2020 Triacontane
 This software is released under the MIT License.
 http://opensource.org/licenses/mit-license.php
----------------------------------------------------------------------------
 Version
 1.5.3 2023/01/31 ゲージを非表示にして現在値を変更してから再表示すると変更前の数値が一瞬表示されてしまう問題を修正
 1.5.2 2022/12/08 ゲージX座標のパラメータ初期値を変更
 1.5.1 2022/11/20 プリセットをtimeにするとラベルが表示されなくなる件を制約事項としてヘルプに記載
 1.5.0 2022/11/19 ラベル部分にアイコンを表示できる機能を追加
 1.4.0 2022/09/11 満タン時のゲージ色を指定できる機能を追加
 1.3.0 2022/08/23 現在値の描画フォーマットを指定できる機能を追加
 1.2.0 2022/05/06 ゲージの表示優先度をピクチャの下に変更できる機能を追加
 1.1.2 2021/10/20 フォント指定のヘルプが誤っていたのを修正
 1.1.1 2021/09/15 コアスクリプトv1.3.3に伴う修正
 1.1.0 2021/04/10 座標に計算式や変数を指定した場合、表示位置やリアルタイムに変更できる機能を追加
 1.0.3 2020/09/16 ゲージ表示後に一度シーンを切り替えてからマップ移動するとゲージピクチャが消えてしまう問題を修正
 1.0.2 2020/09/12 ヘルプのスクリプトの誤記を修正
 1.0,1 2020/08/30 非表示のときは画像を更新しないよう修正
 1.0.0 2020/08/29 初版
----------------------------------------------------------------------------
 [Blog]   : https://triacontane.blogspot.jp/
 [Twitter]: https://twitter.com/triacontane/
 [GitHub] : https://github.com/triacontane/
=============================================================================*/

/*:
 * @plugindesc 汎用ゲージ追加プラグイン
 * @target MZ
 * @url https://github.com/triacontane/RPGMakerMV/tree/mz_master/ExtraGauge.js
 * @base PluginCommonBase
 * @author トリアコンタン
 *
 * @param GaugeList
 * @text ゲージリスト
 * @desc 各画面に追加するゲージのリストです。
 * @default []
 * @type struct<Gauge>[]
 *
 * @param Priority
 * @text 表示優先度
 * @desc ゲージ画像の表示優先度です。マップ画面および戦闘画面のみ『ピクチャの下』に変更できます。
 * @default 0
 * @type select
 * @option 0:通常
 * @value 0
 * @option 1:ピクチャの下
 * @value 1
 *
 * @help ExtraGauge.js
 *
 * 各画面に追加で任意のゲージを好きなだけ表示できます。
 * 現在値や最大値を変数、スクリプトから指定すれば、あとは値の変動に応じて
 * 自動的にゲージが増減します。
 *
 * ゲージは、マップ画面と戦闘画面ではピクチャの上かつウィンドウの下に、
 * それ以外の画面ではウィンドウの上に表示されます。
 *
 * このプラグインの利用にはベースプラグイン『PluginCommonBase.js』が必要です。
 * 『PluginCommonBase.js』は、RPGツクールMZのインストールフォルダ配下の
 * 以下のフォルダに格納されています。
 * dlc/BasicResources/plugins/official
 *
 * フォント指定を利用する場合『FontLoad.js』が必要です。
 * 以下のページの『Raw』ボタンを押下した先から入手してください。
 * https://github.com/triacontane/RPGMakerMV/tree/mz_master/FontLoad.js
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

/*~struct~Gauge:
 *
 * @param SceneName
 * @text 対象シーン
 * @desc 追加対象のシーンです。オリジナルのシーンを対象にする場合はシーンクラス名を直接記入します。
 * @type select
 * @default Scene_Title
 * @option タイトル
 * @value Scene_Title
 * @option マップ
 * @value Scene_Map
 * @option ゲームオーバー
 * @value Scene_Gameover
 * @option バトル
 * @value Scene_Battle
 * @option メインメニュー
 * @value Scene_Menu
 * @option アイテム
 * @value Scene_Item
 * @option スキル
 * @value Scene_Skill
 * @option 装備
 * @value Scene_Equip
 * @option ステータス
 * @value Scene_Status
 * @option オプション
 * @value Scene_Options
 * @option セーブ
 * @value Scene_Save
 * @option ロード
 * @value Scene_Load
 * @option ゲーム終了
 * @value Scene_End
 * @option ショップ
 * @value Scene_Shop
 * @option 名前入力
 * @value Scene_Name
 * @option デバッグ
 * @value Scene_Debug
 *
 * @param Id
 * @text 識別子
 * @desc ゲージの識別子です。特に使用されませんが、分かりやすい名称を設定すると管理がしやすくなります。
 * @default gauge01
 *
 * @param SwitchId
 * @text 表示スイッチID
 * @desc 指定したスイッチがONの場合のみ画面に表示されます。0を指定すると常に表示されます。
 * @default 0
 * @type switch
 *
 * @param OpacityVariable
 * @text 不透明度変数ID
 * @desc 不透明度を取得する変数番号です。0を指定すると常に不透明度255で表示されます。
 * @default 0
 * @type variable
 *
 * @param Layout
 * @text レイアウト
 * @desc ゲージの表示座標と幅、高さです。スクリプトを使用する場合、変数witch, heightでUIエリアの幅と高さを取得できます。
 * @type struct<Layout>
 * @default {"x":"width / 2","y":"30","width":"width * 0.8","height":"36","GaugeX":"0","GaugeHeight":"0","Vertical":"false"}
 *
 * @param CurrentMethod
 * @text 現在値取得方法
 * @desc ゲージの現在値を取得する方法です。変数、スクリプトのいずれかを設定します。
 * @default {"VariableId":"1","Script":"","FixedValue":""}
 * @type struct<Method>
 *
 * @param MaxMethod
 * @text 最大値取得方法
 * @desc ゲージの最大値を取得する方法です。変数、スクリプト、固定値のいずれかを設定します。
 * @default {"VariableId":"0","Script":"","FixedValue":"100"}
 * @type struct<Method>
 *
 * @param Detail
 * @text 詳細設定
 * @desc ゲージの配置や色などの細かい設定です。
 * @type struct<Detail>
 * @default
 *
 * @param LowerPicture
 * @text 下ピクチャ
 * @desc ゲージの下に表示されるピクチャです。ゲージの中心と画像の中心が合わせて表示されます。
 * @default
 * @type struct<Picture>
 *
 * @param UpperPicture
 * @text 上ピクチャ
 * @desc ゲージの上に表示されるピクチャです。ゲージの中心と画像の中心が合わせて表示されます。
 * @default
 * @type struct<Picture>
 *
 * @param Battler
 * @text バトラー情報
 * @desc ゲージの主体となるバトラー情報の参照方法を指定します。現在値、最大値をスクリプトで決める場合のみ使用します。
 * @default
 * @type struct<Battler>
 */

/*~struct~Layout:
 *
 * @param x
 * @text X座標
 * @desc X座標です。原点は中央です。数値以外を指定した場合はスクリプトとして評価します。
 * @default width / 2
 *
 * @param y
 * @text Y座標
 * @desc Y座標です。原点は中央です。数値以外を指定した場合はスクリプトとして評価します。
 * @default 30
 *
 * @param realTime
 * @text リアルタイム座標反映
 * @desc ゲージを表示後、X座標、Y座標が変更されたときにゲージの位置を再評価します。
 * @default false
 * @type boolean
 *
 * @param width
 * @text 横幅
 * @desc 横幅です。数値以外を指定した場合はスクリプトとして評価します。
 * @default width * 0.8
 *
 * @param height
 * @text 高さ
 * @desc 高さです。数値以外を指定した場合はスクリプトとして評価します。
 * @default 36
 *
 * @param GaugeX
 * @text ゲージX座標
 * @desc ゲージのX座標です。ラベルが長い文字の場合は変更してください。
 * @default 28
 *
 * @param GaugeHeight
 * @text ゲージ高さ
 * @desc ゲージの高さです。0を指定すると全体の高さに合わせられます。
 * @default 0
 *
 * @param Vertical
 * @text 縦ゲージ
 * @desc 有効にすると縦方向ゲージになります。ラベルなども縦方向になるので注意してください。
 * @default false
 * @type boolean
 */

/*~struct~Picture:
 *
 * @param FileName
 * @text ファイル名
 * @desc ピクチャのファイル名です。
 * @default
 * @type file
 * @dir img/pictures
 *
 * @param OffsetX
 * @text X座標補正値
 * @desc X座標の補正値です。
 * @default 0
 * @type number
 * @min -9999
 *
 * @param OffsetY
 * @text Y座標補正値
 * @desc Y座標の補正値です。
 * @default 0
 * @type number
 * @min -9999
 */

/*~struct~Method:
 *
 * @param VariableId
 * @text 取得変数ID
 * @desc ゲージの値を取得する変数番号です。スクリプトより優先して参照されます。
 * @default 0
 * @type variable
 *
 * @param Script
 * @text 取得スクリプト
 * @desc ゲージの値を取得するスクリプトです。固定値より優先して参照されます。
 * @default
 * @type combo
 * @option battler.hp; // HP
 * @option battler.mhp; // 最大HP
 * @option battler.mp; // MP
 * @option battler.mmp; // 最大MP
 * @option battler.tp; // TP
 * @option battler.maxTp(); // 最大MP
 * @option meta.value; // メモ欄[value]の値
 *
 * @param FixedValue
 * @text 固定値
 * @desc ゲージの値を固定値で設定します。現在値に固定値を指定することは推奨しません。
 * @default
 * @type number
 * @min 1
 */

/*~struct~Detail:
 *
 * @param RisingSmoothness
 * @text 上昇中のなめらかさ
 * @desc 大きい数を指定するとゲージがゆっくりと上昇します。
 * @default 1
 * @type number
 * @min 1
 *
 * @param FallingSmoothness
 * @text 下降中のなめらかさ
 * @desc 大きい数を指定するとゲージがゆっくりと下降します。
 * @default 1
 * @type number
 * @min 1
 *
 * @param GaugeColorPreset
 * @text ゲージ色のプリセット
 * @desc ゲージ色をプリセットから簡易指定します。既存のゲージ仕様を流用する関係上、timeに設定するとラベルが表示されません。
 * @default hp
 * @type select
 * @option
 * @option hp
 * @option mp
 * @option tp
 * @option time
 *
 * @param GaugeColorLeft
 * @text ゲージ色(左)
 * @desc 左側のゲージ色です。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 *
 * @param GaugeColorRight
 * @text ゲージ色(右)
 * @desc 右側のゲージ色です。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 *
 * @param GaugeColorFullLeft
 * @text 満タン時のゲージ色(左)
 * @desc 満タン時の左側のゲージ色です。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 *
 * @param GaugeColorFullRight
 * @text 満タン時のゲージ色(右)
 * @desc 満タン時の右側のゲージ色です。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 *
 * @param BackColor
 * @text ゲージ背景色
 * @desc ゲージ背景色です。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 *
 * @param Label
 * @text ラベル
 * @desc ゲージの左に表示されるラベル文字列です。
 * @default
 *
 * @param IconIndex
 * @text アイコン
 * @desc ラベルと一緒に描画されるアイコンです。ラベルと一緒に表示させると重なって表示されるので注意してください。
 * @default 0
 *
 * @param LabelFont
 * @text ラベルフォント
 * @desc ラベルを表示するときのフォント情報です。未指定の場合はゲージのデフォルト値が使用されます。
 * @default
 * @type struct<Font>
 *
 * @param DrawValue
 * @text 現在値を描画する
 * @desc ゲージの右側に現在値を描画します。
 * @default true
 * @type boolean
 *
 * @param ValueFont
 * @text 現在値フォント
 * @desc 現在値を表示するときのフォント情報です。未指定の場合はゲージのデフォルト値が使用されます。
 * @default
 * @type struct<Font>
 *
 * @param ValueFormat
 * @text 現在値フォーマット
 * @desc 現在値を表示する際の表示フォーマットです。%1:現在値 %2:最大値に置き換えられます。
 * @default %1/%2
 *
 * @param FlashIfFull
 * @text 満タン時にフラッシュ
 * @desc ゲージの現在値が最大値以上になるとゲージをフラッシュさせます。
 * @default false
 * @type boolean
 */

/*~struct~Font:
 *
 * @param Face
 * @text フォント名
 * @desc フォント名です。別途フォントロードプラグインが必要です。
 * @default
 * @dir fonts
 *
 * @param Size
 * @text フォントサイズ
 * @desc フォントサイズです。
 * @default 0
 * @type number
 *
 * @param Color
 * @text テキストカラー
 * @desc テキストカラーです。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 * @type number
 *
 * @param OutlineColor
 * @text アウトラインカラー
 * @desc アウトラインカラーです。テキストカラー番号かCSS色指定(rgba(0, 0, 0, 0))を指定します。
 * @default 0
 * @type number
 *
 * @param OutlineWidth
 * @text アウトライン横幅
 * @desc アウトラインの横幅です。
 * @default 0
 * @type number
 */

/*~struct~Battler:
 *
 * @param Type
 * @text バトラー種別
 * @desc ゲージの主体となるバトラーの取得方法です。
 * @default
 * @type select
 * @option アクターID
 * @value ActorId
 * @option パーティの並び順
 * @value PartyIndex
 * @option 敵キャラID
 * @value EnemyId
 * @option 敵グループの並び順(戦闘画面で有効)
 * @value TroopIndex
 * @option メニュー画面で選択したアクター(メニュー詳細画面で有効)
 * @value MenuActor
 *
 * @param ActorId
 * @text アクターID
 * @desc 種別選択で『アクターID』を選択したときのアクターIDです。
 * @default 0
 * @type actor
 *
 * @param EnemyId
 * @text 敵キャラID
 * @desc 種別選択で『敵キャラID』を選択したときの敵キャラIDです。
 * @default 0
 * @type enemy
 *
 * @param Index
 * @text 並び順
 * @desc 種別選択で『パーティの並び順』『敵グループの並び順』を選択したときの並び順です。先頭は[0]です。
 * @default 0
 * @type number
 */

(() => {
    'use strict';
    const script = document.currentScript;
    const param = PluginManagerEx.createParameter(script);
    if (!param.GaugeList) {
        param.GaugeList = [];
    }

    const _Scene_Base_create = Scene_Base.prototype.create;
    Scene_Base.prototype.create = function () {
        _Scene_Base_create.apply(this, arguments);
        if (!(this instanceof Scene_Map)) {
            this.createExtraGauges();
        }
    };

    const _Scene_Map_create = Scene_Map.prototype.create;
    Scene_Map.prototype.create = function () {
        _Scene_Map_create.apply(this, arguments);
        this.createExtraGauges();
    };

    Scene_Base.prototype.createExtraGauges = function () {
        this._extraGauges = this.findExtraGaugeList().map(data => {
            return new Sprite_ExtraGaugeContainer(data, data.Detail || {}, data.Layout || {});
        });
    };

    const _Scene_Base_createWindowLayer = Scene_Base.prototype.createWindowLayer;
    Scene_Base.prototype.createWindowLayer = function () {
        if (this instanceof Scene_Message) {
            this.addExtraGauge();
        }
        _Scene_Base_createWindowLayer.apply(this, arguments);
    };

    const _Scene_Base_start = Scene_Base.prototype.start;
    Scene_Base.prototype.start = function () {
        _Scene_Base_start.apply(this, arguments);
        this.addExtraGauge();
    };

    Scene_Base.prototype.addExtraGauge = function () {
        if (this._extraGaugesAdd) {
            return;
        }
        this._extraGauges.forEach(extraGauge => {
            this.addChildExtraGauge(extraGauge);
        });
        this._extraGaugesAdd = true;
    };

    Scene_Base.prototype.addChildExtraGauge = function (extraGauge) {
        this.addChild(extraGauge);
    };

    if (param.Priority === 1) {
        Scene_Battle.prototype.addChildExtraGauge = function (extraGauge) {
            this._spriteset.addChildExtraGauge(extraGauge);
        };

        Scene_Map.prototype.addChildExtraGauge = function (extraGauge) {
            this._spriteset.addChildExtraGauge(extraGauge);
        };

        Spriteset_Base.prototype.addChildExtraGauge = function (extraGauge) {
            const index = this.getChildIndex(this._pictureContainer);
            this.addChildAt(extraGauge, index);
        };
    }

    Scene_Base.prototype.findExtraGaugeList = function () {
        const currentSceneName = PluginManagerEx.findClassName(this);
        return (param.GaugeList || []).filter(function (data) {
            return data.SceneName === currentSceneName;
        }, this);
    };

    const _Sprite_Gauge_initialize = Sprite_Gauge.prototype.initialize;
    Sprite_Gauge.prototype.initialize = function (data, detail, layout) {
        if (data) {
            this._data = data;
            this._detail = detail;
            this._layout = layout;
        }
        _Sprite_Gauge_initialize.apply(this, arguments);
    };

    /**
     * Sprite_ExtraGaugeContainer
     * 追加ゲージとピクチャを含むコンテナです。
     */
    class Sprite_ExtraGaugeContainer extends Sprite {
        constructor(data, detail, layout) {
            super();
            this._data = data;
            this._detail = detail;
            this._layout = layout;
            this.create();
        }

        create() {
            this._gauge = new Sprite_ExtraGauge(this._data, this._detail, this._layout);
            this._lower = this.createPicture(this._data.LowerPicture);
            this.addChild(this._gauge);
            this._upper = this.createPicture(this._data.UpperPicture);
            this.setupPosition();
            this.update();
        }

        setupPosition() {
            this.x = this._gauge.findLayoutValue(this._layout.x);
            this.y = this._gauge.findLayoutValue(this._layout.y);
        }

        update() {
            this.updateVisibly();
            super.update();
            if (this._layout.realTime) {
                this.setupPosition();
            }
            this.updateOpacity();
        }

        updateVisibly() {
            this.visible = this.isVisible();
        }

        updateOpacity() {
            if (this._detail.OpacityVariable) {
                this.opacity = $gameVariables.value(this._detail.OpacityVariable);
            }
        }

        isVisible() {
            return !this._data.SwitchId || $gameSwitches.value(this._data.SwitchId);
        }

        createPicture(pictureData) {
            if (!pictureData || !pictureData.FileName) {
                return null;
            }
            const sprite = new Sprite();
            sprite.anchor.x = 0.5;
            sprite.anchor.y = 0.5;
            sprite.bitmap = ImageManager.loadPicture(pictureData.FileName);
            sprite.x = pictureData.OffsetX || 0;
            sprite.y = pictureData.OffsetY || 0;
            this.addChild(sprite);
            return sprite;
        }
    }

    /**
     * Sprite_ExtraGauge
     * 追加ゲージを扱うクラスです。
     */
    class Sprite_ExtraGauge extends Sprite_Gauge {
        constructor(data, detail, layout) {
            super(data, detail, layout);
            this.setup(this.findBattler(), this._detail.GaugeColorPreset);
            this.setupPosition();
        }

        findBattler() {
            const battlerData = this._data.Battler;
            if (!battlerData) {
                return $gameParty.menuActor();
            }
            const methodName = `findBattler${battlerData.Type}`;
            if (this[methodName]) {
                return this[methodName](battlerData);
            } else {
                return $gameParty.menuActor();
            }
        }

        findBattlerActorId(battlerData) {
            return $gameActors.actor(battlerData.ActorId);
        }

        findBattlerPartyIndex(battlerData) {
            return $gameParty.members()[battlerData.Index];
        }

        findBattlerEnemyId(battlerData) {
            return new Game_Enemy(battlerData.EnemyId, 0, 0);
        }

        findBattlerTroopIndex(battlerData) {
            return $gameTroop.members()[battlerData.Index];
        }

        updateBitmap() {
            const visible = this.parent ? this.parent.visible : false;
            if (visible) {
                if (!this._prevVisible) {
                    this._value = this._targetValue;
                    this._maxValue = this._targetMaxValue;
                }
                super.updateBitmap();
            }
            this._prevVisible = visible;
        }

        updateFlashing() {
            if (!this._detail.FlashIfFull) {
                return;
            }
            if (this.isFull()) {
                this._flashingCount++;
                if (this._flashingCount % 20 < 10) {
                    this.setBlendColor(this.flashingColor1());
                } else {
                    this.setBlendColor(this.flashingColor2());
                }
            } else {
                this.setBlendColor([0, 0, 0, 0]);
            }
        }

        flashingColor1() {
            return [255, 255, 255, 96];
        }

        flashingColor2() {
            return [255, 255, 255, 64];
        }

        isFull() {
            return this._value >= this._maxValue;
        }

        setupPosition() {
            this.anchor.x = 0.5;
            this.anchor.y = 0.5;
            if (this._layout.Vertical) {
                this.rotation = (270 * Math.PI) / 180;
            }
        }

        bitmapWidth() {
            return this.findLayoutValue(this._layout.width) || super.bitmapWidth();
        }

        bitmapHeight() {
            return this.findLayoutValue(this._layout.height) || super.bitmapHeight();
        }

        textHeight() {
            return this.bitmapHeight();
        }

        gaugeHeight() {
            return this.findLayoutValue(this._layout.GaugeHeight) || this.bitmapHeight();
        }

        gaugeX() {
            return this.findLayoutValue(this._layout.GaugeX) || 0;
        }

        findLayoutValue(value) {
            if (isNaN(value)) {
                try {
                    const width = $dataSystem.advanced.uiAreaWidth;
                    const height = $dataSystem.advanced.uiAreaHeight;
                    return eval(value);
                } catch (e) {
                    console.error(e);
                    return 0;
                }
            } else {
                return value;
            }
        }

        currentValue() {
            return this.findValue(this._data.CurrentMethod);
        }

        currentMaxValue() {
            return Math.max(this.findValue(this._data.MaxMethod), 1)
        }

        findValue(method) {
            if (!method) {
                return 0;
            } else if (method.VariableId) {
                return $gameVariables.value(method.VariableId)
            } else if (method.Script) {
                const battler = this._battler;
                const meta = battler.isActor() ? battler.actor().meta : battler.enemy().meta;
                try {
                    return eval(method.Script);
                } catch (e) {
                    console.error(e);
                    return 0;
                }
            } else {
                return method.FixedValue;
            }
        }

        label() {
            return this._detail.Label || '';
        }

        iconIndex() {
            return this._detail.IconIndex || 0;
        }

        labelColor() {
            return this.findColor(this.findLabelFont().Color, super.labelColor());
        }

        labelOutlineColor() {
            return this.findColor(this.findLabelFont().OutlineColor, super.labelOutlineColor());
        }

        labelOutlineWidth() {
            return this.findLabelFont().OutlineWidth || super.labelOutlineWidth();
        }

        labelFontFace() {
            return this.findLabelFont().Face || super.labelFontFace();
        }

        labelFontSize() {
            return this.findLabelFont().Size || super.labelFontSize();
        }

        findLabelFont() {
            return this._detail.LabelFont || {};
        }

        valueColor() {
            const value = this.currentValue();
            if (value >= 100) {
                return ColorManager.deathColor();
            } else if (value >= 50) {
                return ColorManager.crisisColor();
            } else {
                return ColorManager.normalColor();
            }
            //return this.findColor(this.findValueFont().Color, super.valueColor());
        }

        valueOutlineColor() {
            return this.findColor(this.findValueFont().OutlineColor, super.valueOutlineColor());
        }

        valueOutlineWidth() {
            return this.findValueFont().OutlineWidth || super.valueOutlineWidth();
        }

        valueFontFace() {
            return this.findValueFont().Face || super.valueFontFace();
        }

        valueFontSize() {
            return this.findValueFont().Size || super.valueFontSize();
        }

        findValueFont() {
            return this._detail.ValueFont || {};
        }

        gaugeBackColor() {
            return this.findColor(this._detail.BackColor, super.gaugeBackColor());
        }

        gaugeColor1() {
            const fullColor = this._detail.GaugeColorFullLeft;
            const color = this._detail.GaugeColorLeft;
            return this.findColor(this.isFull() ? (fullColor || color) : color, super.gaugeColor1());
        }

        gaugeColor2() {
            const fullColor = this._detail.GaugeColorFullRight;
            const color = this._detail.GaugeColorRight;
            return this.findColor(this.isFull() ? (fullColor || color) : color, super.gaugeColor2());
        }

        isValid() {
            return true;
        }

        smoothness() {
            if (this._value <= this._targetValue) {
                return this._detail.RisingSmoothness || 1;
            } else {
                return this._detail.FallingSmoothness || 1;
            }
        }

        drawValue() {
            if (this._detail.DrawValue) {
                if (this._detail.ValueFormat) {
                    this.drawCustomValue();
                } else {
                    super.drawValue();
                }
            }
        }

        drawCustomValue() {
            const text = this._detail.ValueFormat.format(this.currentValue(), this.currentMaxValue());
            const width = this.bitmapWidth() - 2;
            const height = this.textHeight();
            this.setupValueFont();
            this.bitmap.drawText(text, 0, 0, width, height, "right");
        }

        findColor(code, defaultColor = null) {
            if (!code) {
                return defaultColor ? defaultColor : ColorManager.normalColor();
            } else if (isNaN(code)) {
                return code;
            } else {
                return ColorManager.textColor(code);
            }
        }

        drawLabel() {
            super.drawLabel();
            const icon = this.iconIndex();
            if (icon) {
                this.drawIcon(icon);
            }
        }

        drawIcon(iconIndex) {
            const bitmap = ImageManager.loadSystem("IconSet");
            const pw = ImageManager.iconWidth;
            const ph = ImageManager.iconHeight;
            const sx = (iconIndex % 16) * pw;
            const sy = Math.floor(iconIndex / 16) * ph;
            const x = this.labelOutlineWidth() / 2;
            const y = (this.bitmap.height - ph) / 2;
            this.bitmap.blt(bitmap, sx, sy, pw, ph, x, y);
        }
    }
})();