/*=============================================================================
 ExtraGauge.js
----------------------------------------------------------------------------
 (C)2020 Triacontane
 This software is released under the MIT License.
 http://opensource.org/licenses/mit-license.php
----------------------------------------------------------------------------
 Version
 1.7.0 2023/05/12 ゲージにオリジナルの画像を指定できる機能を追加
 1.6.1 2023/05/09 不透明度のパラメータが正常に機能していなかった問題を修正
 1.6.0 2023/04/24 マップ、戦闘画面でゲージをウィンドウの上に表示できる機能を追加
 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 （翻译：Zeldashu）汎用ゲージ追加プラグイン
 * @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
 * @option 2:ウィンドウの上
 * @value 2
 *
 * @help ExtraGauge.js
 *
 * 可为各个画面配置变量框，大小值由变量或脚本决定，实时变化。
 * 变量框在地图和战斗场合显示在图片之上，窗口之下。
 * 其他场合则在窗口上方。
 *
 * 需要前置插件『PluginCommonBase.js』， 可在官方DLC文件夹里找到：
 * dlc/BasicResources/plugins/official
 *
 * 想指定字体款式的话，则需要『FontLoad.js』插件。
 * 可在下方这个链接下载：
 * 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 只有当指定的开关打开时才会显示在屏幕上。如果指定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 GaugeEndX
 * @text ゲージ終端X座標
 * @desc 变量框的末尾坐标，如果你想在外侧显示数值，请修改。
 * @default 0
 *
 * @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 GaugeImage
 * @text ゲージ画像
 * @desc 为变量框指定图像，根据数值自动裁剪。启用后忽略颜色等设置。
 * @type file
 * @dir img/pictures
 *
 * @param ScaleAutoAdjust
 * @text 拡大率自動調整
 * @desc 根据变量框尺寸自动调整图像放大率。
 * @type boolean
 * @default true
 *
 * @param GaugeColorPreset
 * @text ゲージ色のプリセット
 * @desc 为变量框指定的颜色。由于使用现有的gauge规格，设置为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
 * @type color
 *
 * @param GaugeColorRight
 * @text ゲージ色(右)
 * @desc 右侧变量框颜色。指定文本颜色编号或CSS颜色指定(rgba(0, 0, 0, 0))。
 * @default 0
 * @type color
 *
 * @param GaugeColorFullLeft
 * @text 満タン時のゲージ色(左)
 * @desc 充能满时左侧的颜色。指定文本颜色编号或CSS颜色指定(rgba(0, 0, 0, 0))。
 * @default 0
 * @type color
 *
 * @param GaugeColorFullRight
 * @text 満タン時のゲージ色(右)
 * @desc 充能满时右侧的颜色。指定文本颜色编号或CSS颜色指定(rgba(0, 0, 0, 0))。
 * @default 0
 * @type color
 *
 * @param BackColor
 * @text ゲージ背景色
 * @desc 变量框背景颜色。指定文本颜色编号或CSS颜色指定(rgba(0, 0, 0, 0))。
 * @default 0
 * @type color
 *
 * @param Label
 * @text ラベル
 * @desc 显示在变量框左侧的标签字符串。
 * @default
 *
 * @param IconIndex
 * @text アイコン
 * @desc 与标签一同绘制的图标。请注意，与标签一起显示的话会重叠显示。
 * @default 0
 * @type icon
 *
 * @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，则这里指定。
 * @default 0
 * @type actor
 *
 * @param EnemyId
 * @text 敵キャラID
 * @desc 若上方选择了敌人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 && param.Priority !== 2) {
            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._data.OpacityVariable) {
                this.opacity = $gameVariables.value(this._data.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();
            if (this._detail.GaugeImage) {
                this.setupImage();
            }
        }

        setupImage() {
            const gauge = new Sprite();
            gauge.bitmap = ImageManager.loadPicture(this._detail.GaugeImage);
            gauge.x = -this.width / 2 + this.gaugeX();
            gauge.y = -this.gaugeHeight() / 2 + (this.height - this.gaugeHeight()) / 2;
            if (this._detail.ScaleAutoAdjust) {
                gauge.bitmap.addLoadListener(() => {
                    gauge.scale.x = (this.width - this.gaugeX() - this.gaugeEndX()) / gauge.width;
                    gauge.scale.y = this.gaugeHeight() / gauge.height;
                });
            }
            this._gaugeImage = gauge;
            this.bitmap.gradientFillRect = new Function();
            this.addChild(gauge);
        }

        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;
        }

        gaugeEndX() {
            return this.findLayoutValue(this._layout.GaugeEndX) || 0;
        }

        drawGaugeRect(x, y, width, height) {
            super.drawGaugeRect(x, y, width - this.gaugeEndX(), height);
            if (this._gaugeImage) {
                this.drawGaugeImage();
            }
        }

        drawGaugeImage() {
            const gauge = this._gaugeImage;
            gauge.bitmap.addLoadListener(() => {
                const rate = this.gaugeRate();
                gauge.setFrame(0, 0, gauge.bitmap.width * rate, gauge.bitmap.height);
            });
        }

        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() {
            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);
        }
    }
})();
