//=============================================================================
// MPP_StateLevel.js
//=============================================================================
// Copyright (c) 2024 Mokusei Penguin
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
//=============================================================================

/*:
 * @target MZ
 * @plugindesc ステートを重ね掛け出来るようにします。
 * @author 木星ペンギン
 * @url
 *
 * @help [version 1.0.0]
 * - 本プラグインはRPGツクールMZ用です。
 * - Japanese help only.
 * - 不具合・競合・機能追加等の要望に対処できない場合があります。
 * - これらに対応する場合、有償となります。
 *
 * ▼ 仕様
 *  〇 ステート重ね掛け
 *   - ステートを重ね掛けした場合、そのステートが複数個付与されている状態と
 *     なります。
 *   - ２回重ね掛けした場合、単純に効果が２倍になるとは限りません。
 *     (一部の乗算処理に関しては２倍になるように調整しています)
 *   - 細かな仕様はツクール側の仕様や他プラグインの仕様に準じます。
 *
 *  〇 他追加機能
 *   - ステートに重ねて表示される数値は重ね掛けした回数です。
 *   - ステートの下に表示されるゲージは残り効果時間です。
 *   - ステートを上書きした場合、効果時間が減らないように修正。
 *
 * ================================
 * Mail : wood_penguin＠yahoo.co.jp (＠は半角)
 * Blog : http://woodpenguin.blog.fc2.com/
 * License : MIT license
 *
 */

'use strict';

(() => {
    const pluginName = 'MPP_StateLevel';

    // Plugin Parameters
    const parameters = PluginManager.parameters(pluginName);

    const __base = (obj, prop) => {
        if (obj.hasOwnProperty(prop)) {
            return obj[prop];
        } else {
            const proto = Object.getPrototypeOf(obj);
            return function () { return proto[prop].apply(this, arguments); };
        }
    };

    let stateUser;

    //-------------------------------------------------------------------------
    // Game_Temp

    const _Game_Temp_initialize = Game_Temp.prototype.initialize;
    Game_Temp.prototype.initialize = function() {
        _Game_Temp_initialize.apply(this, arguments);
        stateUser  = null;
    };

    //-------------------------------------------------------------------------
    // Game_Action

    const _Game_Action_itemEffectAddAttackState = Game_Action.prototype.itemEffectAddAttackState;
    Game_Action.prototype.itemEffectAddAttackState = function(target, effect) {
        stateUser = this.subject();
        _Game_Action_itemEffectAddAttackState.apply(this, arguments);
        stateUser = null;
    };

    const _Game_Action_itemEffectAddNormalState = Game_Action.prototype.itemEffectAddNormalState;
    Game_Action.prototype.itemEffectAddNormalState = function(target, effect) {
        stateUser = this.subject();
        _Game_Action_itemEffectAddNormalState.apply(this, arguments);
        stateUser = null;
    };

    //-------------------------------------------------------------------------
    // Game_BattlerBase

    const _Game_BattlerBase_clearStates = Game_BattlerBase.prototype.clearStates;
    Game_BattlerBase.prototype.clearStates = function() {
        _Game_BattlerBase_clearStates.apply(this, arguments);
        this._stateLevels = {};
        this._stateMaxTurns = {};
    };

    const _Game_BattlerBase_eraseState = Game_BattlerBase.prototype.eraseState;
    Game_BattlerBase.prototype.eraseState = function(stateId) {
        _Game_BattlerBase_eraseState.apply(this, arguments);
        delete this._stateLevels[stateId];
        delete this._stateMaxTurns[stateId];
    };

    const _Game_BattlerBase_resetStateCounts = Game_BattlerBase.prototype.resetStateCounts;
    Game_BattlerBase.prototype.resetStateCounts = function(stateId) {
        const stTurns = this._stateTurns;
        const stLevels = this._stateLevels;
        const lastStateTurn = stTurns[stateId] || 0;
        _Game_BattlerBase_resetStateCounts.apply(this, arguments);
        stTurns[stateId] = Math.max(stTurns[stateId], lastStateTurn);
        if (stLevels[stateId]) {
            const maxLevel = this.maxStateLevel(stateId)
                + (stateUser ? stateUser.stateLevelPlus(stateId) : 0);
            if (stLevels[stateId] < maxLevel)
                stLevels[stateId]++;
        } else {
            stLevels[stateId] = 1;
        }
    };

    Game_BattlerBase.prototype.maxStateLevel = function(stateId) {
        const state = $dataStates[stateId];
        return state ? 99 : 1;
    };

    Game_BattlerBase.prototype.displayedStates = function() {
        return this.states().filter(state => state.iconIndex > 0);
    };

    Game_BattlerBase.prototype.stateTurn = function(stateId) {
        return this._stateTurns[stateId] || 0;
    };

    Game_BattlerBase.prototype.maxStateTurn = function(stateId) {
        return this._stateMaxTurns[stateId] || 1;
    };

    Game_BattlerBase.prototype.allLevels = function() {
        const levels = this.displayedStates().map(state => {
            return this.maxStateLevel(state.id) > 1
                ? this._stateLevels[state.id]
                : 0;
        });
        // for (let i = 0; i < this._buffs.length; i++) {
        //     if (this._buffs[i] !== 0) {
        //         levels.push(Math.abs(this._buffs[i]));
        //     }
        // }
        return levels;
    };

    // overwrite
    Game_BattlerBase.prototype.traitObjects = function() {
        return this.levelStates();
    };

    Game_BattlerBase.prototype.levelStates = function() {
        return this.states().reduce((r, state) => {
            const level = this._stateLevels[state.id] || 1;
            for (let i = 0; i < level; i++)
                r.push(state);
            return r;
        }, []);
    };

    // overwrite
    Game_BattlerBase.prototype.traitsPi = function(code, id) {
        const traitMap = new Map();
        const stateTraits = this.states().map(state => state.traits).flat();
        let value = this.traitsWithId(code, id).reduce((r, trait) => {
            if (stateTraits.includes(trait)){
                traitMap.set(trait, (traitMap.get(trait) || 0) + 1);
                return r;
            }
            return r * trait.value;
        }, 1);
        for (const [trait, level] of traitMap)
            value *= (trait.value - 1) * level + 1;
        return value;
    };

    Game_BattlerBase.prototype.allMetadata = function(name) {
        return this.traitObjects().reduce((r, obj) => {
            if (name in obj.meta)
                r.push(obj.meta[name]);
            return r;
        }, []);
    };

    Game_BattlerBase.prototype.metadataNumbers = function(name) {
        return this.allMetadata(name).map(Number);
    };

    Game_BattlerBase.prototype.metadataSum = function(name) {
        return this.metadataNumbers(name).reduce((r, n) => r + n, 0);
    };

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

    const _Game_Battler_addState = Game_Battler.prototype.addState;
    Game_Battler.prototype.addState = function(stateId) {
        _Game_Battler_addState.apply(this, arguments);
        if (this.isStateAffected(stateId))
            this.resetMaxStateTurn(stateId);
    };

    Game_Battler.prototype.resetMaxStateTurn = function(stateId) {
        this._stateMaxTurns[stateId] = Math.max(
            this._stateMaxTurns[stateId] || 0, this._stateTurns[stateId]
        );
    };

    Game_Battler.prototype.stateLevelPlus = function(stateId) {
        return this.metadataSum(`stLvPlus ${stateId}`);
    };

    //-------------------------------------------------------------------------
    // Sprite_StateTurnGauge

    class Sprite_StateTurnGauge extends Sprite_Gauge {
        constructor() {
            super();
            this._stateId = 0;
            this.anchor.set(0.5);
        }

        bitmapWidth() {
            return ImageManager.iconWidth;
        }

        bitmapHeight() {
            return 8;
        }

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

        gaugeHeight() {
            return this.bitmapHeight() - 2;
        }

        gaugeX() {
            return 0;
        }

        setup(battler) {
            super.setup(battler, 'stTurn');
        }

        setState(stateId) {
            if (this._stateId !== stateId) {
                this._stateId = stateId;
                this._value = NaN;
                this._maxValue = NaN;
            }
        }

        currentValue() {
            const state = $dataStates[this._stateId];
            return this._battler && state && state.autoRemovalTiming > 0
                ? this._battler.stateTurn(this._stateId)
                : NaN;
        }

        currentMaxValue() {
            return this._battler
                ? this._battler.maxStateTurn(this._stateId)
                : NaN;
        }

        gaugeColor1() {
            return ColorManager.ctGaugeColor1();
        }

        gaugeColor2() {
            return ColorManager.ctGaugeColor2();
        }

        drawLabel() {}

        drawValue() {}

    }

    //-------------------------------------------------------------------------
    // Sprite_StateIcon

    const _Sprite_StateIcon_initialize = Sprite_StateIcon.prototype.initialize;
    Sprite_StateIcon.prototype.initialize = function() {
        _Sprite_StateIcon_initialize.apply(this, arguments);
        this._level = 0;
        this._lastLevel = 0;
        this.createTurnSprite();
        this.createLevelSprite();
    };

    Sprite_StateIcon.prototype.createTurnSprite = function() {
        this._turnSprite = new Sprite_StateTurnGauge();
        this._turnSprite.move(0, 20);
        this.addChild(this._turnSprite);
    };

    Sprite_StateIcon.prototype.createLevelSprite = function() {
        const bitmap = new Bitmap(ImageManager.iconWidth, ImageManager.iconHeight);
        bitmap.fontFace = $gameSystem.numberFontFace();
        bitmap.fontSize = ImageManager.iconHeight - 12;
        bitmap.textColor = ColorManager.normalColor();
        bitmap.outlineColor = 'rgba(80, 64, 64, 0.75)';//ColorManager.outlineColor();
        bitmap.outlineWidth = 5;
        this._levelSprite = new Sprite(bitmap);
        this._levelSprite.move(-12, -12);
        this.addChild(this._levelSprite);
    };

    const _Sprite_StateIcon_destroy = __base(Sprite_StateIcon.prototype, 'destroy');
    Sprite_StateIcon.prototype.destroy = function() {
        this._levelSprite.bitmap.destroy();
        _Sprite_StateIcon_destroy.apply(this, arguments);
    };

    const _Sprite_StateIcon_setup = Sprite_StateIcon.prototype.setup;
    Sprite_StateIcon.prototype.setup = function(battler) {
        if (this._battler !== battler)
            this._turnSprite.setup(battler);
        _Sprite_StateIcon_setup.apply(this, arguments);
    };

    const _Sprite_StateIcon_update = Sprite_StateIcon.prototype.update;
    Sprite_StateIcon.prototype.update = function() {
        _Sprite_StateIcon_update.apply(this, arguments);
        if (this._lastLevel !== this._level) {
            this._lastLevel = this._level;
            this.updateLevel();
        }
    };

    const _Sprite_StateIcon_updateIcon = Sprite_StateIcon.prototype.updateIcon;
    Sprite_StateIcon.prototype.updateIcon = function() {
        _Sprite_StateIcon_updateIcon.apply(this, arguments);
        const levels = this.shouldDisplay() ? this._battler.allLevels() : [];
        const state = this._battler.displayedStates()[this._animationIndex];
        this._level = levels.length ? levels[this._animationIndex] || 0 : 0;
        this._turnSprite.setState(state ? state.id : 0);
    };

    Sprite_StateIcon.prototype.updateLevel = function() {
        const bitmap = this._levelSprite.bitmap;
        const width = ImageManager.iconWidth;
        const height = ImageManager.iconHeight - 12;
        bitmap.clear();
        if (this._level > 0)
            bitmap.drawText(this._level, 0, 12, width - 2, height, 'right');
    };

})();
