code:
//=============================================================================
// ItemBook.js
//=============================================================================

/*:
 * @plugindesc Displays detailed statuses of items.
 * @author Yoji Ojima
 *
 * @param Unknown Data
 * @desc The index name for an unknown item.
 * @default ??????
 *
 * @param Price Text
 * @desc The text for "Price".
 * @default Price
 *
 * @param Equip Text
 * @desc The text for "Equip".
 * @default Equip
 *
 * @param Type Text
 * @desc The text for "Type".
 * @default Type
 *
 * @help
 *
 * Plugin Command:
 *   ItemBook open            # Open the item book screen
 *   ItemBook add weapon 3    # Add weapon #3 to the item book
 *   ItemBook add armor 4     # Add armor #4 to the item book
 *   ItemBook remove armor 5  # Remove armor #5 from the item book
 *   ItemBook remove item 6   # Remove item #6 from the item book
 *   ItemBook complete        # Complete the item book
 *   ItemBook clear           # Clear the item book
 *
 * Item (Weapon, Armor) Note:
 *   <book:no>                # This item does not appear in the item book
 */

/*:ja
 * @plugindesc アイテム図鑑です。アイテムの詳細なステータスを表示します。
 * @author Yoji Ojima
 *
 * @param Unknown Data
 * @desc 未確認のアイテムの索引名です。
 * @default ？？？？？？
 *
 * @param Price Text
 * @desc 「価格」の文字列です。
 * @default 価格
 *
 * @param Equip Text
 * @desc 「装備」の文字列です。
 * @default 装備
 *
 * @param Type Text
 * @desc 「タイプ」の文字列です。
 * @default タイプ
 *
 * @help
 *
 * プラグインコマンド:
 *   ItemBook open            # 図鑑画面を開く
 *   ItemBook add weapon 3    # 武器３番を図鑑に追加
 *   ItemBook add armor 4     # 防具４番を図鑑に追加
 *   ItemBook remove armor 5  # 防具５番を図鑑から削除
 *   ItemBook remove item 6   # アイテム６番を図鑑から削除
 *   ItemBook complete        # 図鑑を完成させる
 *   ItemBook clear           # 図鑑をクリアする
 *
 * アイテム（武器、防具）のメモ:
 *   <book:no>                # 図鑑に載せない場合
 */

(function() {

    var parameters = PluginManager.parameters('ItemBook');
    var unknownData = String(parameters['Unknown Data'] || '??????');
    var priceText = String(parameters['Price Text'] || 'Price');
    var equipText = String(parameters['Equip Text'] || 'Equip');
    var typeText = String(parameters['Type Text'] || 'Type');

    var _Game_Interpreter_pluginCommand =
            Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        _Game_Interpreter_pluginCommand.call(this, command, args);
        if (command === 'ItemBook') {
            switch (args[0]) {
            case 'open':
                SceneManager.push(Scene_ItemBook);
                break;
            case 'add':
                $gameSystem.addToItemBook(args[1], Number(args[2]));
                break;
            case 'remove':
                $gameSystem.removeFromItemBook(args[1], Number(args[2]));
                break;
            case 'complete':
                $gameSystem.completeItemBook();
                break;
            case 'clear':
                $gameSystem.clearItemBook();
                break;
            }
        }
    };

    Game_System.prototype.addToItemBook = function(type, dataId) {
        if (!this._ItemBookFlags) {
            this.clearItemBook();
        }
        var typeIndex = this.itemBookTypeToIndex(type);
        if (typeIndex >= 0) {
            this._ItemBookFlags[typeIndex][dataId] = true;
        }
    };

    Game_System.prototype.removeFromItemBook = function(type, dataId) {
        if (this._ItemBookFlags) {
            var typeIndex = this.itemBookTypeToIndex(type);
            if (typeIndex >= 0) {
                this._ItemBookFlags[typeIndex][dataId] = false;
            }
        }
    };

    Game_System.prototype.itemBookTypeToIndex = function(type) {
        switch (type) {
        case 'item':
            return 0;
        case 'weapon':
            return 1;
        case 'armor':
            return 2;
        default:
            return -1;
        }
    };

    Game_System.prototype.completeItemBook = function() {
        var i;
        this.clearItemBook();
        for (i = 1; i < $dataItems.length; i++) {
            this._ItemBookFlags[0][i] = true;
        }
        for (i = 1; i < $dataWeapons.length; i++) {
            this._ItemBookFlags[1][i] = true;
        }
        for (i = 1; i < $dataArmors.length; i++) {
            this._ItemBookFlags[2][i] = true;
        }
    };

    Game_System.prototype.clearItemBook = function() {
        this._ItemBookFlags = [[], [], []];
    };

    Game_System.prototype.isInItemBook = function(item) {
        if (this._ItemBookFlags && item) {
            var typeIndex = -1;
            if (DataManager.isItem(item)) {
                typeIndex = 0;
            } else if (DataManager.isWeapon(item)) {
                typeIndex = 1;
            } else if (DataManager.isArmor(item)) {
                typeIndex = 2;
            }
            if (typeIndex >= 0) {
                return !!this._ItemBookFlags[typeIndex][item.id];
            } else {
                return false;
            }
        } else {
            return false;
        }
    };

    var _Game_Party_gainItem = Game_Party.prototype.gainItem;
    Game_Party.prototype.gainItem = function(item, amount, includeEquip) {
        _Game_Party_gainItem.call(this, item, amount, includeEquip);
        if (item && amount > 0) {
            var type;
            if (DataManager.isItem(item)) {
                type = 'item';
            } else if (DataManager.isWeapon(item)) {
                type = 'weapon';
            } else if (DataManager.isArmor(item)) {
                type = 'armor';
            }
            $gameSystem.addToItemBook(type, item.id);
        }
    };

    function Scene_ItemBook() {
        this.initialize.apply(this, arguments);
    }

    Scene_ItemBook.prototype = Object.create(Scene_MenuBase.prototype);
    Scene_ItemBook.prototype.constructor = Scene_ItemBook;

    Scene_ItemBook.prototype.initialize = function() {
        Scene_MenuBase.prototype.initialize.call(this);
    };

    Scene_ItemBook.prototype.create = function() {
        Scene_MenuBase.prototype.create.call(this);
        this._indexWindow = new Window_ItemBookIndex(0, 0);
        this._indexWindow.setHandler('cancel', this.popScene.bind(this));
        var wy = this._indexWindow.height;
        var ww = Graphics.boxWidth;
        var wh = Graphics.boxHeight - wy;
        this._statusWindow = new Window_ItemBookStatus(0, wy, ww, wh);
        this.addWindow(this._indexWindow);
        this.addWindow(this._statusWindow);
        this._indexWindow.setStatusWindow(this._statusWindow);
    };

    function Window_ItemBookIndex() {
        this.initialize.apply(this, arguments);
    }

    Window_ItemBookIndex.prototype = Object.create(Window_Selectable.prototype);
    Window_ItemBookIndex.prototype.constructor = Window_ItemBookIndex;

    Window_ItemBookIndex.lastTopRow = 0;
    Window_ItemBookIndex.lastIndex  = 0;

    Window_ItemBookIndex.prototype.initialize = function(x, y) {
        var width = Graphics.boxWidth;
        var height = this.fittingHeight(6);
        Window_Selectable.prototype.initialize.call(this, x, y, width, height);
        this.refresh();
        this.setTopRow(Window_ItemBookIndex.lastTopRow);
        this.select(Window_ItemBookIndex.lastIndex);
        this.activate();
    };

    Window_ItemBookIndex.prototype.maxCols = function() {
        return 3;
    };

    Window_ItemBookIndex.prototype.maxItems = function() {
        return this._list ? this._list.length : 0;
    };

    Window_ItemBookIndex.prototype.setStatusWindow = function(statusWindow) {
        this._statusWindow = statusWindow;
        this.updateStatus();
    };

    Window_ItemBookIndex.prototype.update = function() {
        Window_Selectable.prototype.update.call(this);
        this.updateStatus();
    };

    Window_ItemBookIndex.prototype.updateStatus = function() {
        if (this._statusWindow) {
            var item = this._list[this.index()];
            this._statusWindow.setItem(item);
        }
    };

    Window_ItemBookIndex.prototype.refresh = function() {
        var i, item;
        this._list = [];
        for (i = 1; i < $dataItems.length; i++) {
            item = $dataItems[i];
            if (item.name && item.itypeId === 1 && item.meta.book !== 'no') {
                this._list.push(item);
            }
        }
        for (i = 1; i < $dataWeapons.length; i++) {
            item = $dataWeapons[i];
            if (item.name && item.meta.book !== 'no') {
                this._list.push(item);
            }
        }
        for (i = 1; i < $dataArmors.length; i++) {
            item = $dataArmors[i];
            if (item.name && item.meta.book !== 'no') {
                this._list.push(item);
            }
        }
        this.createContents();
        this.drawAllItems();
    };

    Window_ItemBookIndex.prototype.drawItem = function(index) {
        var item = this._list[index];
        var rect = this.itemRect(index);
        var width = rect.width - this.textPadding();
        if ($gameSystem.isInItemBook(item)) {
            this.drawItemName(item, rect.x, rect.y, width);
        } else {
            var iw = Window_Base._iconWidth + 4;
            this.drawText(unknownData, rect.x + iw, rect.y, width - iw);
        }
    };

    Window_ItemBookIndex.prototype.processCancel = function() {
        Window_Selectable.prototype.processCancel.call(this);
        Window_ItemBookIndex.lastTopRow = this.topRow();
        Window_ItemBookIndex.lastIndex = this.index();
    };

    function Window_ItemBookStatus() {
        this.initialize.apply(this, arguments);
    }

    Window_ItemBookStatus.prototype = Object.create(Window_Base.prototype);
    Window_ItemBookStatus.prototype.constructor = Window_ItemBookStatus;

    Window_ItemBookStatus.prototype.initialize = function(x, y, width, height) {
        Window_Base.prototype.initialize.call(this, x, y, width, height);
    };

    Window_ItemBookStatus.prototype.setItem = function(item) {
        if (this._item !== item) {
            this._item = item;
            this.refresh();
        }
    };

    Window_ItemBookStatus.prototype.refresh = function() {
        var item = this._item;
        var x = 0;
        var y = 0;
        var lineHeight = this.lineHeight();

        this.contents.clear();

        if (!item || !$gameSystem.isInItemBook(item)) {
            return;
        }

        this.drawItemName(item, x, y);

        x = this.textPadding();
        y = lineHeight + this.textPadding();

        var price = item.price > 0 ? item.price : '-';
        this.changeTextColor(this.systemColor());
        this.drawText(priceText, x, y, 120);
        this.resetTextColor();
        this.drawText(price, x + 120, y, 120, 'right');
        y += lineHeight;

        if (DataManager.isWeapon(item) || DataManager.isArmor(item)) {
            var etype = $dataSystem.equipTypes[item.etypeId];
            this.changeTextColor(this.systemColor());
            this.drawText(equipText, x, y, 120);
            this.resetTextColor();
            this.drawText(etype, x + 120, y, 120, 'right');
            y += lineHeight;

            var type;
            if (DataManager.isWeapon(item)) {
                type = $dataSystem.weaponTypes[item.wtypeId];
            } else {
                type = $dataSystem.armorTypes[item.atypeId];
            }
            this.changeTextColor(this.systemColor());
            this.drawText(typeText, x, y, 120);
            this.resetTextColor();
            this.drawText(type, x + 120, y, 120, 'right');

            x = this.textPadding() + 300;
            y = lineHeight + this.textPadding();
            for (var i = 2; i < 8; i++) {
                this.changeTextColor(this.systemColor());
                this.drawText(TextManager.param(i), x, y, 160);
                this.resetTextColor();
                this.drawText(item.params[i], x + 160, y, 60, 'right');
                y += lineHeight;
            }
        }

        x = 0;
        y = this.textPadding() * 2 + lineHeight * 7;
        this.drawTextEx(item.description, x, y);
    };

})();


code:
//=============================================================================
// ItemDescription.js
// ----------------------------------------------------------------------------
// Version
// 0.8.0 2019/10/13 試験公開（完全版は出るかもしれないし出ないかもしれない）
/*:
 * @plugindesc アイテム説明文自動生成プラグイン
 * @author 饗庭淵
 *
 * @param validWeapon
 * @text 武器
 * @desc 武器に対して説明文の自動生成を有効にします
 * @type boolean
 * @on 有効
 * @off 無効
 * @default true
 *
 * @param validArmor
 * @text 防具
 * @desc 防具に対して説明文の自動生成を有効にします
 * @type boolean
 * @on 有効
 * @off 無効
 * @default true
 *
 * @param exceptionWeaponList
 * @text 例外武器リスト
 * @desc プラグインの対象にしたくない武器のリストです（IDを入力）
 * @type string[]
 * @default []
 *
 * @param exceptionArmorList
 * @text 例外防具リスト
 * @desc プラグインの対象にしたくない防具のリストです（IDを入力）
 * @type string[]
 * @default []
 *
 * @param exceptionElementList
 * @text 例外付与属性リスト
 * @desc 説明文に追加したくない属性のリストです（IDを入力）
 * （すべての武器に付与される「物理」属性など）
 * @type string[]
 * @default []
 *
 * @param 用語設定
 * @default ====================================
 *
 * @param enchant
 * @text 付与
 * @desc 属性や状態異常を付与するリストを並べる際の頭につける表示です
 * @default 付
 * @parent 用語設定
 *
 * @param resist
 * @text 耐性
 * @desc 属性や状態異常の耐性リストを並べる際の頭につける表示です
 * @default 耐
 * @parent 用語設定
 *
 * @param weak
 * @text 弱点
 * @desc 属性や状態異常の弱点リストを並べる際の頭につける表示です
 * @default 弱
 * @parent 用語設定
 *
 * @param hit
 * @text 命中率
 * @desc 命中率の表示名です（空白にした場合は表示しません）
 * @default 命中
 * @parent 用語設定
 *
 * @param eva
 * @text 回避率
 * @desc 回避率の表示名です（空白にした場合は表示しません）
 * @default 回避
 * @parent 用語設定
 *
 * @param cri
 * @text 会心率
 * @desc 会心率の表示名です（空白にした場合は表示しません）
 * @default 会心
 * @parent 用語設定
 *
 * @param cev
 * @text 会心回避率
 * @desc 会心回避率の表示名です（空白にした場合は表示しません）
 * @default 会心回避
 * @parent 用語設定
 *
 * @param mev
 * @text 魔法回避率
 * @desc 魔法回避率の表示名です（空白にした場合は表示しません）
 * @default 魔法回避
 * @parent 用語設定
 *
 * @param mrf
 * @text 魔法反射率
 * @desc 魔法反射率の表示名です（空白にした場合は表示しません）
 * @default 魔法反射
 * @parent 用語設定
 *
 * @param cnt
 * @text 反撃率
 * @desc 反撃率の表示名です（空白にした場合は表示しません）
 * @default 反撃
 * @parent 用語設定
 *
 * @param hrg
 * @text HP再生率
 * @desc HP再生率の表示名です（空白にした場合は表示しません）
 * @default HP再生
 * @parent 用語設定
 *
 * @param mrg
 * @text MP再生率
 * @desc MP再生率の表示名です（空白にした場合は表示しません）
 * @default MP再生
 * @parent 用語設定
 *
 * @param trg
 * @text TP再生率
 * @desc TP再生率の表示名です（空白にした場合は表示しません）
 * @default TP再生
 * @parent 用語設定
 *
 * @help
 * --------------------------------
 * ●概要
 * --------------------------------
 * アイテム（武器と防具）の説明文をパラメーター設定から自動生成します。
 * 生成された内容は説明文の二行目以降に追加されます。
 *
 * 手動で説明文を打ちこんでいると「説明文と実際の効果が違うやん！」という
 * ミスが起こりがちです。
 * つまりその手のミスを防ぐものです。いちいち手打ちする手間も省けます。
 *
 * 【例】
 * （「炎の剣」の場合）
 * 狭い室内で振り回すな　すべての剣にいえることだが
 * <剣>[攻撃+20] 付[炎][火傷]
 *
 * （「とても重い鎧」の場合）
 * めちゃくちゃ重いが、そのぶんに防御力は高い
 * <重装防具>[防御+30][敏捷-10][回避-20%]
 *
 * 二行目は自動生成されたもので、一行目は従来通りツクール上で入力されたものです。
 *
 * --------------------------------
 * ●メモ欄
 * --------------------------------
 * また、パラメータ設定の枠に収まらない特殊な説明が必要な場合は
 * メモ欄にメタタグ<option>を追記してください。
 *
 * 【例】
 * （<option:備考[たまに喋る]>と入力）
 * 狭い室内で振り回すな　すべての剣にいえることだが
 * <剣>[攻撃+20] 付[炎][火傷] 備考[たまに喋る]
 *
 * --------------------------------
 * ●注意点
 * --------------------------------
 * 本プラグインは機械的に説明文を追加するものであるため
 * 効果が多く設定されていると説明欄からはみ出す場合があります。
 * そういった場合は「例外リスト」に登録し、そのアイテムだけ手動で入力する
 * というような使い方になると思います。
 *
 * また、説明文を二行書いていると自動生成された説明文は三行目に追加されることに
 * なるため、本プラグインを適用しただけでははみ出ることになります。
 *
 * 現在は試験的に「武器」と「防具」だけですが、
 * 原理的には「アイテム」「スキル」でも同じことができます。
 * スクリプトがわかる人はコピペして改変した方が使いやすいかもしれません。
 *
 * また、「特殊」設定での「追加能力値」までは対応していますが、
 * 「特殊能力値」までは現在対応していません。
 *
 * --------------------------------
 * ●スクリプトがわかる人向け
 * --------------------------------
 * ヘルプウインドウ以外でも使いたい場合は
 * Window_Base に descriotion(item) や drawItemDescriotion(item, x, y)
 * という関数が定義されていますので、こちらをご利用ください。
 *
 * --------------------------------
 * ●利用規約
 * --------------------------------
 * 特に制限はありません。
 * ご自由にどうぞ。
 */

 (function() {
    var pluginName = "ItemDescription";

    //パラメータを受け取る
    var parameters = PluginManager.parameters(pluginName);

    var validWeapon = Boolean(parameters['validWeapon']);
    var validArmor = Boolean(parameters['validWeapon']);
    var exceptionWeaponList = JSON.parse(parameters['exceptionWeaponList']).map(function(id) {
        return Number(id);
    });
    var exceptionArmorList = JSON.parse(parameters['exceptionArmorList']).map(function(id) {
        return Number(id);
    });
    var exceptionElementList = JSON.parse(parameters['exceptionElementList']).map(function(id) {
        return Number(id);
    });

    var enchant = String(parameters['enchant']);
    var resist = String(parameters['resist']);
    var weak = String(parameters['weak']);

    var xparams = [];
    xparams.push(String(parameters['hit']));
    xparams.push(String(parameters['eva']));
    xparams.push(String(parameters['cri']));
    xparams.push(String(parameters['cev']));
    xparams.push(String(parameters['mev']));
    xparams.push(String(parameters['mrf']));
    xparams.push(String(parameters['cnt']));
    xparams.push(String(parameters['hrg']));
    xparams.push(String(parameters['mrg']));
    xparams.push(String(parameters['trg']));

    TextManager.xparam = function(paramId) {
        return xparams[paramId] || '';
    };

    Window_Help.prototype.setItem = function(item) {
        this.setText(item ? this.descriotion(item) : '');
    };

    Window_Base.prototype.drawItemDescriotion = function(item, x, y) {
        var text = this.descriotion(item);
        this.drawTextEx(text, x, y);
    };

    Window_Base.prototype.descriotion = function(item) {
        var text = item ? item.description : '';
        //if(DataManager.isItem(item)) text = this.itemDescriotion(item);
        if(DataManager.isWeapon(item) && validWeapon && !exceptionWeaponList.includes(item.id)) text = this.weaponDescriotion(item);
        if(DataManager.isArmor(item) && validArmor && !exceptionArmorList.includes(item.id)) text = this.ArmorDescriotion(item);
        return text;
    };

    Window_Base.prototype.itemDescriotion = function(item) {
        var description = JSON.parse(JSON.stringify(item.description)) + '\n';

        return description;
    };

    Window_Base.prototype.weaponDescriotion = function(item) {
        var description = JSON.parse(JSON.stringify(item.description)) + '\n';
        // 種類
        var wtypeId = item.wtypeId;
        description += '<' + $dataSystem.weaponTypes[wtypeId] + '>';

        // パラメーター情報
        var params = item.params;
        var traits = item.traits;
        description += paramList(params) + xparamList(traits);

        // 属性と状態異常の付与
        description += enchantList(traits);

        // 属性と状態異常の耐性
        description += resistList(traits) + weakList(traits);

        // 備考
        description += itemOption(item);
        return description;
    };

    Window_Base.prototype.ArmorDescriotion = function(item) {
        var description = JSON.parse(JSON.stringify(item.description)) + '\n';
        // 種類
        var atypeId = item.atypeId;
        description += '<' + $dataSystem.armorTypes[atypeId] + '>';

        /*
        // 部位
        var etypeId = item.etypeId;
        description += '<' + $dataSystem.equipTypes[etypeId] + '>';
        */

        // パラメーター情報
        var params = item.params;
        var traits = item.traits;
        description += paramList(params) + xparamList(traits);

        // 属性と状態異常
        description += resistList(traits) + weakList(traits);

        // 備考
        description += itemOption(item);
        return description;
    };

    function paramList(params) {
        var description = '';
        for(var i = 0; i < params.length; i++){
            if(params[i] != 0){
                var sign = params[i] > 0 ? '+' : '';
                description += '[' + TextManager.param(i) + sign + params[i] + ']'
            }
        }
        return description;
    };

    function xparamList(traits) {
        var description = '';
        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 22 && traits[i].value != 0){
                var param = Math.floor(traits[i].value * 100);
                var sign = param > 0 ? '+' : '';
                description += '[' + TextManager.xparam(traits[i].dataId) + sign + param + '%]'
            }
        }
        return description;
    };

    function enchantList(traits) {
        var description = '';
        var fore = ' ' + enchant;
        // 属性
        var elemets = $dataSystem.elements;
        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 31 && !exceptionElementList.includes(traits[i].dataId)){
                description += fore + '[' + elemets[traits[i].dataId] + ']'
                fore = '';
            }
        }

        // 状態異常
        var states = $dataStates.map(function(state) {
            return state && state.name;
        });

        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 32){
                description += fore + '[' + states[traits[i].dataId] + ']'
                fore = '';
            }
        }
        return description;
    };

    function resistList(traits) {
        var description = '';
        // 属性
        var elemets = $dataSystem.elements;
        var fore = ' ' + resist;
        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 11 && traits[i].value < 1){
                description += fore + '[' + elemets[traits[i].dataId] + ']'
                fore = '';
            }
        }

        // 状態異常
        var states = $dataStates.map(function(state) {
            return state && state.name;
        });

        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 13 && traits[i].value < 1){
                description += fore + '[' + states[traits[i].dataId] + ']'
                fore = '';
            }
        }
        return description;
    };

    function weakList(traits) {
        var description = '';
        // 属性
        var elemets = $dataSystem.elements;
        var fore = ' ' + weak;;
        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 11 && traits[i].value > 1){
                description += fore + '[' + elemets[traits[i].dataId] + ']'
                fore = '';
            }
        }

        // 状態異常
        var states = $dataStates.map(function(state) {
            return state && state.name;
        });

        for(var i = 0; i < traits.length; i++){
            if(traits[i].code == 13 && traits[i].value > 1){
                description += fore + '[' + states[traits[i].dataId] + ']'
                fore = '';
            }
        }
        return description;
    };

    function itemOption(item) {
        var description = '';
        if(item.meta.option) description += ' ' + item.meta.option;
        return description;
    };
})();

repo_name: onms/sister

license: unknown


code:
//=============================================================================
// KMS_MapActiveMessage.js
//   Last update: 2022/05/22
//=============================================================================

/*:
 * @plugindesc
 * [v0.3.2] Show messages automatically for events on map.
 *
 * @author Kameo (Kamesoft)
 *
 * @param Balloon offset Y
 * @default 20
 * @desc Vertical position offset for balloons by pixel.
 *
 * @param Balloon margin
 * @default -8
 * @desc Adjust frame size of balloons by pixel.
 *
 * @param Default range
 * @default 4
 * @desc Default distance for displaying message by tile unit.
 *
 * @param Display duration
 * @default 300
 * @desc Display frame count.
 *
 * @param Max message count
 * @default 10
 * @desc Max number of active messages.
 *
 * @param Message skin
 * @default ActiveMessageSkin
 * @require 1
 * @dir img/system/
 * @type file
 * @desc Message skin for active message. Load from img/system.
 *
 *
 * @help
 *
 * ## Plugin command
 *
 * MapActiveMessage enable      # Enable active message
 * MapActiveMessage disable     # Display active message
 * MapActiveMessage show 10     # Show message of the event whose ID is 10
 * MapActiveMessage show        # Show messages of all events
 *
 */

/*:ja
 * @plugindesc
 * [v0.3.2] プレイヤーが近付いたときに、自動的にメッセージを表示するイベントを作成します。
 *
 * @author かめお (Kamesoft)
 *
 * @param Balloon offset Y
 * @default 20
 * @desc 吹き出しの縦方向の位置をピクセル単位で調整します。
 *
 * @param Balloon margin
 * @default -8
 * @desc 吹き出しの枠サイズをピクセル単位で調整します。
 *
 * @param Default range
 * @default 4
 * @desc メッセージを表示する距離をタイル単位で指定します。
 *
 * @param Display duration
 * @default 300
 * @desc メッセージ表示時間をフレーム単位で指定します。
 *
 * @param Max message count
 * @default 10
 * @desc 表示できるメッセージの最大数です。
 *
 * @param Message skin
 * @default ActiveMessageSkin
 * @require 1
 * @dir img/system/
 * @type file
 * @desc メッセージの表示に使用するスキン画像です。 img/system から読み込みます。
 *
 *
 * @help
 *
 * ■ プラグインコマンド
 *
 * MapActiveMessage enable      # アクティブメッセージを有効にします。
 * MapActiveMessage disable     # アクティブメッセージを無効にします。
 * MapActiveMessage show 10     # イベント ID:10 のメッセージを表示します。
 * MapActiveMessage show        # 全イベントのメッセージを表示します。
 *
 */

var KMS = KMS || {};

(function() {

'use strict';

// 定数
var Const =
{
    debug:      false,              // デバッグモード
    pluginCode: 'MapActiveMessage', // プラグインコード
    regex: {
        activeMessage:   /<(?:アクティブメッセージ|ActiveMessage)\s*[:\s]\s*([^>]+)>/i,
        displayRange:    /<(?:アクティブメッセージ距離|ActiveMessageRange)\s*[:\s]\s*(\d+)>/i,
        displayDuration: /<(?:アクティブメッセージ表示時間|ActiveMessageDuration)\s*[:\s]\s*(\d+)>/i,
        loopMessage:     /<(?:アクティブメッセージループ|ActiveMessageLoop)>/i,
        beginMessage:    /<(?:アクティブメッセージ|ActiveMessage)\s*[:\s][^>]+$/i,
        endMessage:      /([^>]*)>/
    },

    balloonFrameInfo:  { margin: 4, size: 24, lineLength: 48, srcSize: 96 },
    balloonVertexSize: { width: 24, height: 48 },

    // 吹き出しの表示位置
    balloonPosition: { below: 0, above: 1 }
};

var PluginName = 'KMS_' + Const.pluginCode;

KMS.imported = KMS.imported || {};
KMS.imported[Const.pluginCode] = true;

function isNullOrUndefined(value)
{
    return value === null || value === undefined;
}

// デフォルト値つきで文字列から int を解析
function parseIntWithDefault(param, defaultValue)
{
    var value = parseInt(param);
    return isNaN(value) ? defaultValue : value;
}

var pluginParams = PluginManager.parameters(PluginName);
var Params = {};
Params.messageSkin     = pluginParams['Message skin'] || 'ActiveMessageSkin';
Params.messageCountMax = Math.max(parseIntWithDefault(pluginParams['Max message count'], 8), 1);
Params.balloonOffsetY  = parseIntWithDefault(pluginParams['Balloon offset Y'], 20);
Params.balloonMargin   = parseIntWithDefault(pluginParams['Balloon margin'], -8);
Params.defaultRange    = Math.max(parseIntWithDefault(pluginParams['Default range'], 4), 1);
Params.displayDuration = Math.max(parseIntWithDefault(pluginParams['Display duration'], 300), 1);
Params.fadeFrameCount  = Math.max(parseIntWithDefault(pluginParams['Fade frame count'], 8), 1);
Params.fontSize        = Math.max(parseIntWithDefault(pluginParams['Font size'], 18), 1);

// デバッグログ
var debuglog;
if (Const.debug)
{
    debuglog = function() { console.log(arguments); };
}
else
{
    debuglog = function() { };
}

var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args)
{
    _Game_Interpreter_pluginCommand.call(this, command, args);

    if (command !== Const.pluginCode)
    {
        return;
    }

    switch (args[0])
    {
    case 'enable':      // 有効化
        $gameSystem.setMapActiveMessageEnabled(true);
        break;

    case 'disable':     // 無効化
        $gameSystem.setMapActiveMessageEnabled(false);
        break;

    case 'show':        // 強制表示 (eventId)
        // eventId 省略時 (undefined) は全イベントが対象
        $gameMap.forceDisplayActiveMessage(parseInt(args[1]));
        break;

    default:
        // 不明なコマンド
        console.error('[%s %s] Unknown command.', Const.pluginCode, args[0]);
        break;
    }
};

// タイリング描画
function tileBlt(dstImage, srcImage, sx, sy, sw, sh, dx, dy, dw, dh)
{
    for (var offY = 0; offY < dh; offY += sh)
    {
        for (var offX = 0; offX < dw; offX += sw)
        {
            var tempSh = Math.min(sh, dh - offY);
            var tempSw = Math.min(sw, dw - offX);
            dstImage.blt(
                srcImage,
                sx, sy, tempSw, tempSh,
                dx + offX, dy + offY, tempSw, tempSh
            );
        }
    }
}

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

var _Game_Temp_initialize = Game_Temp.prototype.initialize;
Game_Temp.prototype.initialize = function()
{
    _Game_Temp_initialize.call(this);

    this.clearMapActiveMessage();
};

/**
 * アクティブメッセージの登録
 */
Game_Temp.prototype.pushMapActiveMessages = function(targetEvent, messages, needsLoop, isForced)
{
    var message =
    {
        id: this._mapActiveMessageNextId++,
        event: targetEvent,
        messages: messages,
        needsLoop: needsLoop,
        isForced: isForced
    };
    this._mapActiveMessages.push(message);
};

/**
 * アクティブメッセージのクリア
 */
Game_Temp.prototype.clearMapActiveMessage = function()
{
    this._mapActiveMessageNextId = 0;
    this._mapActiveMessages = [];
};

/**
 * 次のアクティブメッセージを取得
 */
Game_Temp.prototype.popNextMapActiveMessage = function()
{
    return this._mapActiveMessages.shift();
};

/**
 * アクティブメッセージが登録されているか
 */
Game_Temp.prototype.isMapActiveMessageReady = function()
{
    return this._mapActiveMessages.length > 0;
};

//-----------------------------------------------------------------------------
// Game_System

/**
 * アクティブメッセージの有効状態を取得
 */
Game_System.prototype.isMapActiveMessageEnabled = function()
{
    return !isNullOrUndefined(this._mapActiveMessageEnabled) ?
        this._mapActiveMessageEnabled :
        true;
};

/**
 * アクティブメッセージの有効状態を設定
 */
Game_System.prototype.setMapActiveMessageEnabled = function(enabled)
{
    this._mapActiveMessageEnabled = !!enabled;
};

//-----------------------------------------------------------------------------
// Game_Map

var _Game_Map_setup = Game_Map.prototype.setup;
Game_Map.prototype.setup = function(mapId)
{
    _Game_Map_setup.call(this, mapId);

    this.initializeActiveMessage();
};

/**
 * アクティブメッセージの初期化
 */
Game_Map.prototype.initializeActiveMessage = function()
{
    // 最初は全イベントの表示済みフラグを解除
    this.clearActiveMessageShownFlags();
};

/**
 * アクティブメッセージの表示済みフラグを解除
 */
Game_Map.prototype.clearActiveMessageShownFlags = function()
{
    this.events().forEach(function(event)
    {
        event.setMapActiveMessageShown(false);
    });

    this.requestUpdateActiveMessage();
};

/**
 * アクティブメッセージの更新要求
 */
Game_Map.prototype.requestUpdateActiveMessage = function()
{
    this._needsUpdateActiveMessage = true;
};

var _Game_Map_update = Game_Map.prototype.update;
Game_Map.prototype.update = function(sceneActive)
{
    _Game_Map_update.call(this, sceneActive);

    this.updateDisplayActiveMessage();
};

/**
 * アクティブメッセージの表示判定
 */
Game_Map.prototype.updateDisplayActiveMessage = function()
{
    if (!this._needsUpdateActiveMessage)
    {
        return;
    }

    this._needsUpdateActiveMessage = false;

    // アクティブメッセージの登録
    var events = $gamePlayer.findAvailableMapActiveMessageEvents();
    events.forEach(function(event)
    {
        $gameTemp.pushMapActiveMessages(
            event,
            event.getMapActiveMessages(),
            event.needsMapActiveMessageLoop());
    });
};

/**
 * アクティブメッセージの強制表示
 */
Game_Map.prototype.forceDisplayActiveMessage = function(eventId)
{
    var invoker = function(event)
    {
        $gameTemp.pushMapActiveMessages(
            event,
            event.getMapActiveMessages(),
            event.needsMapActiveMessageLoop(),
            true);
    };

    if (isNullOrUndefined(eventId) || isNaN(eventId))
    {
        // 全表示
        this.events().forEach(invoker);
    }
    else
    {
        // イベント ID 指定
        this.events().filter(function(event)
        {
            return event.eventId() === eventId;
        }).forEach(invoker);
    }
};

var _Game_Map_refresh = Game_Map.prototype.refresh;
Game_Map.prototype.refresh = function()
{
    _Game_Map_refresh.call(this);

    this.requestUpdateActiveMessage();
};

//-----------------------------------------------------------------------------
// Game_CharacterBase

var _Game_CharacterBase = Game_CharacterBase.prototype.setDirection;
Game_CharacterBase.prototype.setDirection = function(d)
{
    _Game_CharacterBase.call(this, d);

    // 移動時には方向指定があるはずなので、ここでメッセージの更新要求を出す
    if (!isNullOrUndefined($gameMap))
    {
        $gameMap.requestUpdateActiveMessage();
    }
};

//-----------------------------------------------------------------------------
// Game_Player

/**
 * アクティブメッセージを表示可能なイベントを探す
 */
Game_Player.prototype.findAvailableMapActiveMessageEvents = function()
{
    if (!this.canMove())
    {
        return [];
    }

    // 指定距離内でメッセージ未表示のイベントを列挙
    var candidateEvents = $gameMap.events().filter(function(event)
    {
        return event.hasMapActiveMessage() &&
            !event.isMapActiveMessageShown() &&
            this.calcDistanceForMapActiveMessage(event) <= event.getMapActiveMessageRange();
    }, this);

    // 表示済みフラグの解除判定
    $gameMap.events().forEach(function(event)
    {
        var distance = this.calcDistanceForMapActiveMessage(event);
        if (event.isMapActiveMessageShown())
        {
            // メッセージ表示距離から出ていれば表示済みフラグ解除
            if (distance > event.getMapActiveMessageRange())
            {
                event.setMapActiveMessageShown(false);
            }
        }
    }, this);

    return candidateEvents;
};

Game_Player.prototype.calcDistanceForMapActiveMessage = function(event)
{
    var diffX = this.x - event.x;
    var diffY = this.y - event.y;

    return Math.sqrt(diffX * diffX + diffY * diffY);
};

//-----------------------------------------------------------------------------
// Game_Event

var _Game_Event_setupPage = Game_Event.prototype.setupPage;
Game_Event.prototype.setupPage = function()
{
    _Game_Event_setupPage.call(this);

    this.setupMapActiveMessage();
};

/**
 * 現在のページ番号を取得
 */
Game_Event.prototype.pageIndex = function()
{
    return this._pageIndex;
};

/**
 * アクティブメッセージを構築
 */
Game_Event.prototype.setupMapActiveMessage = function()
{
    this._mapActiveMessageList      = [];
    this._mapActiveMessageRange     = Params.defaultRange;
    this._mapActiveMessageDuration  = Params.displayDuration;
    this._needsMapActiveMessageLoop = false;
    this._isMapActiveMessageShown   = false;

    var page = this.page();
    if (isNullOrUndefined(page))
    {
        return;
    }

    var parsed = this.parseCommentForMapActiveMessage(page);
    var mergedComment = parsed.mergedComment;
    var commentUnits = parsed.commentUnits;

    // メッセージ距離を解析
    var rangeMatch = Const.regex.displayRange.exec(mergedComment);
    if (!isNullOrUndefined(rangeMatch))
    {
        this._mapActiveMessageRange =
            Math.max(parseIntWithDefault(rangeMatch[1], Params.defaultRange), 1);
    }

    // ループ設定を解析
    var loopMatch = Const.regex.loopMessage.exec(mergedComment);
    if (!isNullOrUndefined(loopMatch))
    {
        this._needsMapActiveMessageLoop = true;
    }

    commentUnits.forEach(
        function (unit)
        {
            // メッセージ定義を解析
            var messageMatch = Const.regex.activeMessage.exec(unit);
            if (!isNullOrUndefined(messageMatch))
            {
                this._mapActiveMessageList.push(
                    {
                        text: messageMatch[1],
                        duration: this._mapActiveMessageDuration
                    });
            }

            // メッセージ表示時間を解析
            var durationMatch = Const.regex.displayDuration.exec(unit);
            if (!isNullOrUndefined(durationMatch))
            {
                var duration = parseIntWithDefault(durationMatch[1], Params.displayDuration);
                this._mapActiveMessageDuration = duration;

                // 直前のメッセージの duration を変更
                if (this._mapActiveMessageList.length > 0)
                {
                    this._mapActiveMessageList[this._mapActiveMessageList.length - 1]
                        .duration = duration;
                }
            }
        }, this);
};

/**
 * アクティブメッセージ用のコメント解析
 *
 * @param {Object}  page    解析対象のイベントページ
 */
Game_Event.prototype.parseCommentForMapActiveMessage = function(page)
{
    function isComment(command)
    {
        return command && (command.code === 108 || command.code === 408);
    }

    // 注釈以外に達するまで解析
    var commandIndex  = 0;
    var command       = page.list[commandIndex];
    var mergedComment = '';  // コメントを結合した文字列
    var commentUnit   = '';
    var commentUnits  = [];  // 解析単位ごとに結合した文字列のリスト
    var inMessage     = false;
    while (isComment(command))
    {
        if (mergedComment.length > 0)
        {
            mergedComment += '\n';
        }

        var commentText = command.parameters[0];
        mergedComment += commentText;

        if (inMessage)
        {
            if (commentUnit.length > 0)
            {
                commentUnit += '\n';
            }

            commentUnit += commentText;

            if (Const.regex.endMessage.test(commentText))
            {
                commentUnits.push(commentUnit);
                commentUnit = '';
                inMessage = false;
            }
        }
        else
        {
            if (Const.regex.beginMessage.test(commentText))
            {
                commentUnit = commentText;
                inMessage = true;
            }
            else
            {
                commentUnits.push(commentText);
            }
        }

        command = page.list[++commandIndex];
    }

    if (inMessage)
    {
        commentUnits.push(commentUnit);
    }

    return {
        mergedComment: mergedComment,
        commentUnits: commentUnits
    };
}

/**
 * アクティブメッセージを取得
 *
 * @return {String} メッセージ
 */
Game_Event.prototype.getMapActiveMessages = function()
{
    return this._mapActiveMessageList;
};

/**
 * アクティブメッセージが存在するか
 *
 * @return {Boolean} true if exists
 */
Game_Event.prototype.hasMapActiveMessage = function()
{
    return this._mapActiveMessageList.length !== 0;
};

/**
 * アクティブメッセージを表示可能な範囲
 *
 * @return {Number} タイル単位の表示範囲半径
 */
Game_Event.prototype.getMapActiveMessageRange = function()
{
    return this._mapActiveMessageRange;
};

/**
 * アクティブメッセージをループするか
 *
 * @return {Boolean} true if needs loop
 */
Game_Event.prototype.needsMapActiveMessageLoop = function()
{
    return this._needsMapActiveMessageLoop;
};

/**
 * アクティブメッセージを表示するフレーム数
 *
 * @return {Number} 自動消去するまでのフレーム数
 */
Game_Event.prototype.getMapActiveMessageDuration = function()
{
    return this._mapActiveMessageDuration;
};

/**
 * アクティブメッセージを表示済みか
 *
 * @return {Boolean} true if shown
 */
Game_Event.prototype.isMapActiveMessageShown = function()
{
    return this._isMapActiveMessageShown;
};

/**
 * アクティブメッセージ表示済みかどうかを設定
 *
 * @param {Boolean} shown 設定する値
 */
Game_Event.prototype.setMapActiveMessageShown = function(shown)
{
    this._isMapActiveMessageShown = !!shown;
};

//-----------------------------------------------------------------------------
// Sprite_MapActiveMessageBalloon
//
// アクティブメッセージの吹き出し用スプライト

function Sprite_MapActiveMessageBalloon()
{
    this.initialize.apply(this, arguments);
}

Sprite_MapActiveMessageBalloon.prototype = Object.create(Sprite_Base.prototype);
Sprite_MapActiveMessageBalloon.prototype.constructor = Sprite_MapActiveMessageBalloon;

Sprite_MapActiveMessageBalloon.prototype.initialize = function()
{
    Sprite_Base.prototype.initialize.call(this);

    this.anchor.y = 0.5;
    this.initMembers();
};

Sprite_MapActiveMessageBalloon.prototype.initMembers = function()
{
    this._owner = null;
};

/**
 * オーナーウィンドウの設定
 */
Sprite_MapActiveMessageBalloon.prototype.setOwner = function(owner)
{
    this._owner = owner;
};

/**
 * 位置をオーナーウィンドウに合わせる
 */
Sprite_MapActiveMessageBalloon.prototype.followOwner = function()
{
    this.y = this._owner.height / 2;
};

/**
 * サイズをオーナーウィンドウに合わせる
 */
Sprite_MapActiveMessageBalloon.prototype.fitToOwner = function()
{
    if (isNullOrUndefined(this._owner))
    {
        return;
    }

    var frameSize    = Const.balloonFrameInfo.size;
    var vertexHeight = Const.balloonVertexSize.height;
    var heightOffset = (vertexHeight - frameSize) * 2;

    // Bitmap のサイズ調整
    var bitmapWidth = this._owner.width;
    var bitmapHeight = this._owner.height + heightOffset;
    if (isNullOrUndefined(this.bitmap) ||
        (this.width != bitmapWidth || this.height != bitmapHeight))
    {
        this.bitmap = new Bitmap(bitmapWidth, bitmapHeight);
    }
    else
    {
        this.bitmap.clear();
    }

    if (Const.debug)
    {
        this.bitmap.fillRect(0, 0, bitmapWidth, bitmapHeight, 'rgba(160, 255, 160, 0.5)');
    }
};

/**
 * 吹き出しの再描画
 */
Sprite_MapActiveMessageBalloon.prototype.refresh = function()
{
    if (isNullOrUndefined(this._owner))
    {
        return;
    }

    // サイズの調整
    this.fitToOwner();

    // 吹き出しの描画
    var frameSize    = Const.balloonFrameInfo.size;
    var vertexHeight = Const.balloonVertexSize.height;
    var offsetX      = -Params.balloonMargin;
    var offsetY      = vertexHeight - frameSize - Params.balloonMargin;
    this.drawBalloon(
        offsetX,
        offsetY,
        this.width  - offsetX * 2,
        this.height - offsetY * 2
    );
};

/**
 * 吹き出しを描画
 */
Sprite_MapActiveMessageBalloon.prototype.drawBalloon = function(x, y, width, height)
{
    if (isNullOrUndefined(this._owner))
    {
        return;
    }

    this.drawBalloonBackground(x, y, width, height);
    this.drawBalloonFrame(x, y, width, height);
};

/**
 * 吹き出しの背景を描画
 */
Sprite_MapActiveMessageBalloon.prototype.drawBalloonBackground = function(x, y, width, height)
{
    var srcImage = this._owner.getWindowSkin();
    var frame    = Const.balloonFrameInfo;

    var prevOpacity = this.bitmap.paintOpacity;
    this.bitmap.paintOpacity = this._owner.backOpacity;

    // 背景下層
    this.bitmap.blt(
        srcImage,
        0, 0, frame.srcSize, frame.srcSize,
        x + frame.margin, y + frame.margin, width - frame.margin * 2, height - frame.margin * 2
    );

    // 背景上層
    tileBlt(
        this.bitmap, srcImage,
        0, frame.srcSize, frame.srcSize, frame.srcSize,
        x + frame.margin, y + frame.margin, width - frame.margin * 2, height - frame.margin * 2
    );

    this.bitmap.paintOpacity = prevOpacity;
};

/**
 * 吹き出しの枠を描画
 */
Sprite_MapActiveMessageBalloon.prototype.drawBalloonFrame = function(x, y, width, height)
{
    var srcImage     = this._owner.getWindowSkin();
    var vertexOffset = this._owner.getBalloonVertexX();
    var balloonPos   = this._owner.getBalloonPosition();

    var frame      = Const.balloonFrameInfo;
    var vertexSize = Const.balloonVertexSize;

    // 吹き出しの頂点は枠内に収める
    vertexOffset = Math.min(
        Math.max(vertexOffset, 0),
        width - (frame.size * 2 + vertexSize.width)
    );

    // 縦枠 + コーナーを描画
    function drawFrameV(sx, dx, dy)
    {
        this.bitmap.blt(
            srcImage,
            sx, 0, frame.size, frame.size,
            dx, dy, frame.size, frame.size
        );
        this.bitmap.blt(
            srcImage,
            sx, frame.size, frame.size, frame.lineLength,
            dx, dy + frame.size, frame.size, height - frame.size * 2
        );
        this.bitmap.blt(
            srcImage,
            sx, frame.size + frame.lineLength, frame.size, frame.size,
            dx, dy + height - frame.size, frame.size, frame.size
        );
    }

    // 横枠と頂点を描画
    function drawFrameH(sy, dx, dy, showVertex)
    {
        var baseSx     = frame.srcSize + frame.size;
        var frameWidth = width - frame.size * 2;
        var leftWidth  = showVertex ? vertexOffset : frameWidth;

        // 枠線の左側 (頂点がない場合は全体)
        this.bitmap.blt(
            srcImage,
            baseSx, sy, frame.lineLength, frame.size,
            dx, dy, leftWidth, frame.size
        );

        if (!showVertex)
        {
            // 頂点を描画しない
            return;
        }

        // sy > 0 なら下枠、sy === 0 なら上枠のはず
        var newDx = dx + leftWidth;
        var newDy = dy + (sy > 0 ? 0 : -(vertexSize.height - frame.size));

        // 頂点
        var vertexSx = frame.srcSize + (sy > 0 ? 0 : vertexSize.width);
        this.bitmap.blt(
            srcImage,
            vertexSx, frame.srcSize, vertexSize.width, vertexSize.height,
            newDx, newDy, vertexSize.width, vertexSize.height
        );

        if (Const.debug)
        {
            this.bitmap.fillRect(newDx, newDy, vertexSize.width, vertexSize.height, 'rgba(255, 160, 160, 0.5)');
        }

        // 枠線の右側
        var rightWidth = frameWidth - (leftWidth + vertexSize.width);
        if (rightWidth > 0)
        {
            newDx += vertexSize.width;
            this.bitmap.blt(
                srcImage,
                baseSx, sy, frame.lineLength, frame.size,
                newDx, dy, rightWidth, frame.size
            );
        }
    }

    // 左枠
    drawFrameV.call(this, frame.srcSize, x, y);

    // 右枠
    drawFrameV.call(this, frame.srcSize + frame.size + frame.lineLength, x + width - frame.size, y);

    // 上枠 + 頂点
    drawFrameH.call(
        this,
        0, x + frame.size, y,
        balloonPos === Const.balloonPosition.below
    );

    // 下枠 + 頂点
    drawFrameH.call(
        this,
        frame.size + frame.lineLength, x + frame.size, y + height - frame.size,
        balloonPos === Const.balloonPosition.above
    );
};

//-----------------------------------------------------------------------------
// Sprite_Character

/**
 * 同一のイベントか判定
 */
Sprite_Character.prototype.isSameEvent = function(event)
{
    if (isNullOrUndefined(this._character) ||
        isNullOrUndefined(event) ||
        !(this._character instanceof Game_Event))
    {
        return false;
    }

    return this._character.eventId() === event.eventId();
};

//-----------------------------------------------------------------------------
// Spriteset_Map

/**
 * 指定されたイベントに対応するキャラクタースプライトを取得
 */
Spriteset_Map.prototype.findCharacterSpriteByEvent = function(event)
{
    var sameSprites = this._characterSprites.filter(function(sprite)
    {
        return sprite.isSameEvent(event);
    });

    if (sameSprites.length > 0)
    {
        return sameSprites[0];
    }
    else
    {
        return null;
    }
};

//-----------------------------------------------------------------------------
// Window_MapActiveMessage
//
// マップ上でアクティブメッセージを表示するウィンドウ
// (スプライトでも良いが、メッセージ周りはウィンドウの方が扱いやすい)

function Window_MapActiveMessage()
{
    this.initialize.apply(this, arguments);
}

Window_MapActiveMessage.prototype = Object.create(Window_Base.prototype);
Window_MapActiveMessage.prototype.constructor = Window_MapActiveMessage;

// メッセージの一時描画バッファ
Window_MapActiveMessage.tempMessageBuffer = null;

/**
 * アクティブメッセージ用スキンの事前読み込み
 */
Window_MapActiveMessage.preloadWindowskin = function()
{
    ImageManager.loadSystem(Params.messageSkin);
};

Window_MapActiveMessage.prototype.initialize = function()
{
    // コンテンツサイズは内容に応じて可変なので、ウィンドウサイズは仮
    Window_Base.prototype.initialize.call(this, 0, 0, 64, 64);

    if (isNullOrUndefined(Window_MapActiveMessage.tempMessageBuffer))
    {
        Window_MapActiveMessage.tempMessageBuffer =
            new Bitmap(Graphics.boxWidth, Graphics.boxHeight / 2);
    }

    this._balloonSprite = null;

    this.opacity         = 0;
    this.contentsOpacity = 0;
    this.initMembers();
};

Window_MapActiveMessage.prototype.standardFontSize = function()
{
    return Params.fontSize;
};

Window_MapActiveMessage.prototype.loadWindowskin = function()
{
    // デフォルトのスキンの代わりに、アクティブメッセージ用のスキンを読み込む
    this._windowskin = ImageManager.loadSystem(Params.messageSkin);
};

/**
 * ウィンドウスキンを取得
 */
Window_MapActiveMessage.prototype.getWindowSkin = function()
{
    return this._windowskin;
};

Window_MapActiveMessage.prototype.initMembers = function()
{
    this._message         = null;
    this._pendingMessages = [];
    this._textState       = null;
    this._event           = null;
    this._characterSprite = null;
    this._targetPageIndex = 0;
    this._duration        = 0;
    this._isHiding        = false;
    this._balloonPosition = Const.balloonPosition.above;
};

Window_MapActiveMessage.prototype.printDebug = function(message)
{
    if (Const.debug)
    {
        console.log('[MapActiveMessage] ' + this.getId() + ': ' + message);
    }
};

Window_MapActiveMessage.prototype.show = function()
{
    Window_Base.prototype.show.call(this);

    if (!isNullOrUndefined(this._balloonSprite))
    {
        this._balloonSprite.show();
    }
};

Window_MapActiveMessage.prototype.hide = function()
{
    // ロールバック処理中はメッセージ無しで呼ばれる
    if (this._message)
    {
        this.printDebug('Hide');
    }

    this._duration = 0;
    this._isHiding = true;

    Window_Base.prototype.hide.call(this);

    if (!isNullOrUndefined(this._balloonSprite))
    {
        this._balloonSprite.hide();
    }
};

/**
 * フェードアウト
 */
Window_MapActiveMessage.prototype.fadeOut = function()
{
    if (this.isFadingOut())
    {
        return;
    }

    this.printDebug('Fading out');

    this._isHiding = true;

    if (this.isFadingIn())
    {
        // フェードイン中は同じ不透明度からフェードアウトする
        this._duration = this._initialDisplayDuration - this._duration;
    }
    else
    {
        this._duration = Params.fadeFrameCount;
    }
};

Window_MapActiveMessage.prototype.numVisibleRows = function()
{
    return 4;
};

/**
 * 吹き出しスプライトの設定
 */
Window_MapActiveMessage.prototype.setBalloonSprite = function(sprite)
{
    sprite.setOwner(this);
    this._balloonSprite = sprite;

    // ウィンドウの裏に表示
    this.addChildAt(sprite, 0);
};

/**
 * イベントに対する吹き出しの表示位置を取得
 */
Window_MapActiveMessage.prototype.getBalloonPosition = function()
{
    return this._balloonPosition;
};

/**
 * メッセージ表示中か
 */
Window_MapActiveMessage.prototype.isDisplaying = function()
{
    return this._duration > 0;
};

/**
 * メッセージ ID を取得
 */
Window_MapActiveMessage.prototype.getId = function()
{
    return this._message.id;
};

/**
 * ループ表示するか
 */
Window_MapActiveMessage.prototype.needsLoop = function()
{
    return this._message.needsLoop;
};

/**
 * 強制表示されたか
 */
Window_MapActiveMessage.prototype.isForced = function()
{
    return this._message.isForced;
};

/**
 * フェードイン中か
 */
Window_MapActiveMessage.prototype.isFadingIn = function()
{
    return this._duration >= this._initialDisplayDuration - Params.fadeFrameCount;
};

/**
 * フェードアウト中か
 */
Window_MapActiveMessage.prototype.isFadingOut = function()
{
    return this._duration < Params.fadeFrameCount;
};

/**
 * 消去されているか
 */
Window_MapActiveMessage.prototype.isHiding = function ()
{
    return this._isHiding;
};

/**
 * 表示中のイベントと同一イベントか
 */
Window_MapActiveMessage.prototype.isSameEvent = function(event)
{
    if (isNullOrUndefined(this._event))
    {
        return false;
    }

    // ページ番号まで同じ場合に同一と見なす
    if (this._event.eventId() === event.eventId() &&
        this._targetPageIndex === event.pageIndex())
    {
        return true;
    }

    return false;
};

/**
 * イベントが画面内にあるか判定
 */
Window_MapActiveMessage.prototype.isEventInScreen = function()
{
    if (isNullOrUndefined(this._event))
    {
        return false;
    }

    var x = this._event.screenX();
    var y = this._event.screenY();
    return x >= 0 && y >= 0 && x <= Graphics.boxWidth && y <= Graphics.boxHeight;
};

/**
 * メッセージの表示開始
 *
 * @param {String}              message     表示するメッセージ
 * @param {Sprite_Character}    sprite      表示対象のキャラクタースプライト
 */
Window_MapActiveMessage.prototype.display = function(message, sprite)
{
    if (isNullOrUndefined(message))
    {
        throw "'message' must not be null or undefined.";
    }

    // 表示のキャンセル
    function rollback()
    {
        this.initMembers();
        this.hide();
    }

    if (message.messages.length <= 0 || isNullOrUndefined(message.event))
    {
        rollback.call(this);
        return;
    }

    this._message = message;
    this._event = message.event;
    this._characterSprite = sprite;

    if (!this.isEventInScreen())
    {
        rollback.call(this);
        return;
    }

    this._targetPageIndex = this._event.pageIndex();

    this.initializeMessage();
    this.show();

    this.printDebug('Display new message for event ' + this._event.eventId() + '.');

    // 多重表示抑止のため、表示済みフラグを立てる
    this._event.setMapActiveMessageShown(true);
};

/**
 * メッセージの初期化
 */
Window_MapActiveMessage.prototype.initializeMessage = function ()
{
    this._pendingMessages = this._message.messages.slice();

    this.displayNextMessage();
};

/**
 * 次のメッセージを表示
 */
Window_MapActiveMessage.prototype.displayNextMessage = function()
{
    var message = this._pendingMessages.shift();
    if (isNullOrUndefined(message))
    {
        return false;
    }

    this._textState =
    {
        index: 0,
        text:  this.convertEscapeCharacters(message.text)
    };
    this._allTextWidth           = 0;
    this._initialDisplayDuration = message.duration;
    this._duration               = this._initialDisplayDuration;

    this.newPage(this._textState);
    this.updatePosition();
    this.updateOpacity();
    this.refreshBalloon();

    return true;
};

/**
 * 新しいページの表示
 */
Window_MapActiveMessage.prototype.newPage = function(textState)
{
    // コンテンツを一時領域に書く
    this.contents = Window_MapActiveMessage.tempMessageBuffer;
    this.contents.clear();
    this.resetFontSettings();
    textState.x         = this.newLineX();
    textState.y         = 0;
    textState.left      = this.newLineX();
    textState.height    = this.calcTextHeight(textState, false);
    this._allTextHeight = this.calcTextHeight(textState, true);

    this.updateMessage();

    // コンテンツを一時領域から正式に反映
    this.createContents();
    this.contents.blt(
        Window_MapActiveMessage.tempMessageBuffer,
        0, 0, this._allTextWidth, this._allTextHeight,
        0, 0, this._allTextWidth, this._allTextHeight
    );
};

/**
 * メッセージの表示位置を設定
 */
Window_MapActiveMessage.prototype.updatePosition = function()
{
    if (isNullOrUndefined(this._event))
    {
        return;
    }

    // 画面外に出る場合は消去
    if (!this.isEventInScreen())
    {
        this.hide();
        return;
    }

    // 吹き出しの枠ぶんのマージン
    var margin = Const.balloonFrameInfo.size;

    var balloonOffsetY = Const.balloonVertexSize.height
        - Params.balloonOffsetY
        - margin;
    var heightOffset = this.height
        + (!isNullOrUndefined(this._characterSprite) ? this._characterSprite.patternHeight() : $gameMap.tileHeight());

    // デフォルトの位置は上
    var x = this._event.screenX() - this.width / 2;
    var y = this._event.screenY() - (heightOffset + balloonOffsetY);
    this._balloonPosition = Const.balloonPosition.above;

    // 画面左右にははみ出させない
    x = Math.min(Math.max(x, margin), Graphics.boxWidth - this.width - margin);

    // 画面上側にはみ出る場合は下に表示
    if (y < margin)
    {
        y = this._event.screenY() + balloonOffsetY;
        this._balloonPosition = Const.balloonPosition.below;
    }

    // 画面下側にはみ出る場合は上に表示
    if (y + this.height > Graphics.boxHeight - margin)
    {
        y -= heightOffset + balloonOffsetY;
        this._balloonPosition = Const.balloonPosition.above;
    }

    this.x = x;
    this.y = y;

    if (!isNullOrUndefined(this._balloonSprite))
    {
        // 吹き出しの頂点表示を考慮した位置に移動
        this._balloonSprite.followOwner();
    }
};

/**
 * ウィンドウのサイズを更新
 */
Window_MapActiveMessage.prototype.updateWindowSize = function()
{
    var offset = this.standardPadding() * 2;
    this.width  = this._allTextWidth  + offset;
    this.height = this._allTextHeight + offset;
};

/**
 * 不透明度の更新
 */
Window_MapActiveMessage.prototype.updateOpacity = function()
{
    var opacity = 255;
    if (this.isFadingIn())
    {
        // フェードイン
        opacity = 255 * (this._initialDisplayDuration - this._duration) / Params.fadeFrameCount;
    }
    else if (this.isFadingOut())
    {
        // フェードアウト
        opacity = 255 * this._duration / Params.fadeFrameCount;
    }

    this.contentsOpacity = opacity;
    if (!isNullOrUndefined(this._balloonSprite))
    {
        this._balloonSprite.opacity = opacity;
    }
};

/**
 * 吹き出しの再描画
 */
Window_MapActiveMessage.prototype.refreshBalloon = function()
{
    if (!isNullOrUndefined(this._balloonSprite))
    {
        this._balloonSprite.refresh();
    }
};

/**
 * プレイヤーと距離が離れていたら消去する
 *
 * @return {Boolean} true if hidden
 */
Window_MapActiveMessage.prototype.hideFarMessage = function()
{
    // 表示範囲外に出た場合は消す
    var distance = $gamePlayer.calcDistanceForMapActiveMessage(this._event);
    if (distance > this._event.getMapActiveMessageRange())
    {
        this.fadeOut();
        return true;
    }

    return false;
};

/**
 * プレイヤーとの距離を確認
 */
Window_MapActiveMessage.prototype.checkPlayerDistance = function()
{
    // 強制表示 or 消去中は距離判定しない
    if (this.isForced() ||
        this._duration <= Params.fadeFrameCount)
    {
        return;
    }

    this.hideFarMessage();
};

Window_MapActiveMessage.prototype.update = function()
{
    Window_Base.prototype.update.call(this);

    if (!isNullOrUndefined(this._balloonSprite))
    {
        this._balloonSprite.update();
    }

    if (!this.isDisplaying())
    {
        return;
    }

    this._duration--;
    this.updateOpacity();
    this.updatePosition();
    this.checkPlayerDistance();

    if (!this.isDisplaying())
    {
        if (this.displayNextMessage())
        {
            this.printDebug('Display next message.');
        }
        else if (this.needsLoop() && !this.isHiding())
        {
            // 距離が離れていたらループしない
            if (!this.hideFarMessage())
            {
                this.printDebug('Loop message.');
                this.initializeMessage();
            }
        }
        else
        {
            // 表示が終了したら初期化
            this.printDebug('Finished to display.');
            this.initMembers();
        }
    }
};

/**
 * メッセージの更新
 */
Window_MapActiveMessage.prototype.updateMessage = function()
{
    if (isNullOrUndefined(this._textState))
    {
        return false;
    }

    while (!this.isEndOfText(this._textState))
    {
        this.processCharacter(this._textState);
        this._allTextWidth = Math.max(this._allTextWidth, this._textState.x);
    }

    this.updateWindowSize();

    return true;
};

/**
 * 吹き出しの頂点位置を取得
 */
Window_MapActiveMessage.prototype.getBalloonVertexX = function()
{
    if (isNullOrUndefined(this._event))
    {
        return 0;
    }

    return this._event.screenX()
        - this.x
        - Const.balloonFrameInfo.size
        - Const.balloonVertexSize.width / 2
        + Params.balloonMargin;
};

/**
 * 改行時の描画先 X 座標
 */
Window_MapActiveMessage.prototype.newLineX = function()
{
    return 0;
};

/**
 * 改行の処理
 */
Window_MapActiveMessage.prototype.processNewLine = function(textState)
{
    this._allTextWidth = Math.max(this._allTextWidth, textState.x);

    Window_Base.prototype.processNewLine.call(this, textState);
};

/**
 * 改ページの処理
 */
Window_MapActiveMessage.prototype.processNewPage = function(textState)
{
    Window_Base.prototype.processNewPage.call(this, textState);
    if (textState.text[textState.index] === '\n')
    {
        textState.index++;
    }

    textState.y = this.contents.height;
};

/**
 * テキスト終端か
 */
Window_MapActiveMessage.prototype.isEndOfText = function(textState)
{
    return textState.index >= textState.text.length;
};

/**
 * 改ページが必要か
 */
Window_MapActiveMessage.prototype.needsNewPage = function(textState)
{
    return !this.isEndOfText(textState) &&
            textState.y + textState.height > this.contents.height;
};

//-----------------------------------------------------------------------------
// Scene_Map

var _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
Scene_Map.prototype.createAllWindows = function()
{
    _Scene_Map_createAllWindows.call(this);

    this.createActiveMessageWindow();
};

/**
 * アクティブメッセージウィンドウの作成
 */
Scene_Map.prototype.createActiveMessageWindow = function()
{
    if (!isNullOrUndefined(this._activeMessageWindowLayer))
    {
        return;
    }

    var width  = Graphics.boxWidth;
    var height = Graphics.boxHeight;
    var x = (Graphics.width  - width)  / 2;
    var y = (Graphics.height - height) / 2;
    this._activeMessageWindowLayer = new WindowLayer();
    this._activeMessageWindowLayer.move(x, y, width, height);
    this.addChild(this._activeMessageWindowLayer);

    Window_MapActiveMessage.preloadWindowskin();
};

var _Scene_Map_update = Scene_Map.prototype.update;
Scene_Map.prototype.update = function()
{
    _Scene_Map_update.call(this);

    this.updateActiveMessageDisplay();
};

var _Scene_Map_stop = Scene_Map.prototype.stop;
Scene_Map.prototype.stop = function()
{
    _Scene_Map_stop.call(this);

    $gameTemp.clearMapActiveMessage();
};

var _Scene_Map_terminate = Scene_Map.prototype.terminate;
Scene_Map.prototype.terminate = function()
{
    _Scene_Map_terminate.call(this);

    this.destroyActiveMessageWindows();
};

/**
 * アクティブメッセージウィンドウの破棄
 */
Scene_Map.prototype.destroyActiveMessageWindows = function()
{
    this._activeMessageWindowLayer.removeChildren();
    this.removeChild(this._activeMessageWindowLayer);
    this._activeMessageWindowLayer = null;
};

/**
 * アクティブメッセージの表示を更新
 */
Scene_Map.prototype.updateActiveMessageDisplay = function()
{
    if (!$gameSystem.isMapActiveMessageEnabled() ||
        $gameMap.isEventRunning())
    {
        // メッセージを表示できないときは非表示にしつつ各種フラグを解除
        $gameTemp.clearMapActiveMessage();
        $gameMap.clearActiveMessageShownFlags();
        this._activeMessageWindowLayer.children.forEach(function(child)
        {
            child.fadeOut();
        });
        return;
    }

    // 表示が完了したものを削除
    this._activeMessageWindowLayer.children.filter(function(child)
    {
        return !child.isDisplaying();
    }).forEach(function(item)
    {
        this._activeMessageWindowLayer.removeChild(item);
    }, this);

    // 要求されているメッセージを表示できるだけ表示
    while ($gameTemp.isMapActiveMessageReady())
    {
        if (!this.displayActiveMessage())
        {
            break;
        }
    }
};

/**
 * 指定されたイベントのアクティブメッセージが表示されているか判定
 */
Scene_Map.prototype.isActiveMessageDisplayed = function(event)
{
    var layer = this._activeMessageWindowLayer;
    for (var i = 0; i < layer.children.length; i++)
    {
        var window = layer.children[i];
        if (window.isDisplaying() && window.isSameEvent(event))
        {
            return true;
        }
    }

    return false;
};

/**
 * アクティブメッセージの表示
 */
Scene_Map.prototype.displayActiveMessage = function()
{
    var layer = this._activeMessageWindowLayer;

    // 最大数に達していたら表示を諦める
    // (children には吹き出しも含まれるので / 2)
    if (layer.children.length / 2 >= Params.messageCountMax)
    {
        return false;
    }

    // 予約されているメッセージを表示
    var message = $gameTemp.popNextMapActiveMessage();
    while (!isNullOrUndefined(message))
    {
        // まだ表示していないもののみ表示
        if (!this.isActiveMessageDisplayed(message.event))
        {
            var character = this._spriteset.findCharacterSpriteByEvent(message.event);
            var balloon   = new Sprite_MapActiveMessageBalloon();
            var window    = new Window_MapActiveMessage();
            window.setBalloonSprite(balloon);
            window.display(message, character);
            layer.addChild(window);
        }

        message = $gameTemp.popNextMapActiveMessage();
    }

    return true;
};

var _Scene_Map_snapForBattleBackground = Scene_Map.prototype.snapForBattleBackground;
Scene_Map.prototype.snapForBattleBackground = function()
{
    // アクティブメッセージはキャプチャしない
    var layer = this._activeMessageWindowLayer;
    if (!isNullOrUndefined(layer))
    {
        layer.visible = false;
    }

    _Scene_Map_snapForBattleBackground.call(this);

    if (!isNullOrUndefined(layer))
    {
        layer.visible = true;
    }
};

})();


code:
//=============================================================================
// KMS_Minimap.js
//  last update: 2019/01/01
//=============================================================================

/*:
 * @plugindesc
 * [v0.2.0] Display minimap.
 *
 * @author TOMY (Kamesoft)
 *
 * @param Map rect
 * @default 524, 32, 260, 200
 * @desc
 * Specify minimap position and its size.
 * Format: X, Y, Width, Height
 *
 * @param Grid size
 * @default 10
 * @desc Grid size of the minimap in pixel.
 *
 * @param Blink time
 * @default 180
 * @desc Blinking interval of icons on the minimap in frames.
 *
 * @param Foreground color
 * @default rgba(224, 224, 240, 0.6)
 * @desc
 * Format: rgba(Red, Green, Blue, Alpha)
 *
 * @param Background color
 * @default rgba(0, 0, 160, 0.6)
 * @desc
 * Format: rgba(Red, Green, Blue, Alpha)
 *
 * @param Mask style
 * @default 0
 * @desc
 * Mask style for minimap.
 * 0: None  1: Ellipse  2: Rounded rectangle  3, 4: Hex
 *
 * @param Mask radius
 * @default 48
 * @desc
 * The radius of each of the corners.
 * (Rounded rectangle style only)
 *
 * @help
 * ## Disable minimap
 *
 * Add <NoMinimap> to the note field in a map, the minimap will be disabled on that map.
 *
 *
 * ## Plugin command
 *
 * Minimap show     # Show minimap. If <NoMinimap> is specified, the minimap can't be shown.
 * Minimap hide     # Hide minimap.
 * Minimap refresh  # Redraw minimap.
 *
 * @requiredAssets img/system/MinimapPlayerIcon
 * @requiredAssets img/system/MinimapObjectIcon
 */

/*:ja
 * @plugindesc
 * [v0.2.0] 画面上にミニマップを表示します。
 *
 * @author TOMY (Kamesoft)
 *
 * @param Map rect
 * @default 524, 32, 260, 200
 * @desc
 * ミニマップの表示位置とサイズをピクセル単位で指定します。
 * 書式: X, Y, 幅, 高さ
 *
 * @param Grid size
 * @default 10
 * @desc ミニマップの 1 マスのサイズをピクセル単位で指定します。
 *
 * @param Blink time
 * @default 180
 * @desc ミニマップ上のアイコンを点滅させる時間をフレーム単位で指定します。
 *
 * @param Foreground color
 * @default rgba(224, 224, 240, 0.6)
 * @desc
 * ミニマップの通行可能領域の色を CSS カラーで指定します。
 * 書式: rgba(赤, 緑, 青, 不透明度)
 *
 * @param Background color
 * @default rgba(0, 0, 160, 0.6)
 * @desc
 * ミニマップの通行不可領域の色を CSS カラーで指定します。
 * 書式: rgba(赤, 緑, 青, 不透明度)
 *
 * @param Mask style
 * @default 0
 * @desc
 * ミニマップの表示領域のマスク方法を指定します。
 * 0: なし  1: 楕円  2: 角丸矩形  3: 六角形1  4: 六角形2
 *
 * @param Mask radius
 * @default 48
 * @desc
 * 角丸矩形マスクを指定した場合の、角の丸め具合を指定します。
 *
 * @help
 * ## ミニマップの非表示
 *
 * マップのメモ欄に <NoMinimap> を追加すると、そのマップではミニマップが表示されなくなります。
 *
 *
 * ## プラグインコマンド
 *
 * Minimap show     # ミニマップを表示します。<NoMinimap> を指定したマップでは表示できません。
 * Minimap hide     # ミニマップを隠します。
 * Minimap refresh  # ミニマップを再描画します。
 *
 * @requiredAssets img/system/MinimapPlayerIcon
 * @requiredAssets img/system/MinimapObjectIcon
 */

var KMS = KMS || {};

(function()
{

'use strict';

KMS.imported = KMS.imported || {};
KMS.imported['Minimap'] = true;

/**
 * 矩形を表す文字列を解析
 */
var parseRect = function(str)
{
    var rectReg = /([-]?\d+),\s*([-]?\d+),\s*([-]?\d+),\s*([-]?\d+)/;
    var rect = new PIXI.Rectangle(524, 32, 260, 200);
    var match = rectReg.exec(str);
    if (match)
    {
        rect.x      = Number(match[1]);
        rect.y      = Number(match[2]);
        rect.width  = Number(match[3]);
        rect.height = Number(match[4]);
    }

    return rect;
};

var pluginParams = PluginManager.parameters('KMS_Minimap');
var Params = {};
Params.mapRect = parseRect(pluginParams['Map rect']);
Params.gridSize = Number(pluginParams['Grid size'] || 10);
Params.blinkTime = Number(pluginParams['Blink time'] || 180);
Params.foregroundColor = pluginParams['Foreground color'] || 'rgba(224, 224, 240, 0.6)';
Params.backgroundColor = pluginParams['Background color'] || 'rgba(  0,   0, 160, 0.6)';
Params.playerIconImage = pluginParams['Player icon'] || 'MinimapPlayerIcon';
Params.objectIconImage = pluginParams['Object icon'] || 'MinimapObjectIcon';
Params.displayTileImage = pluginParams['Display tile'];
Params.maskStyle = Number(pluginParams['Mask style'] || 0);
Params.maskRadius = Number(pluginParams['Mask radius'] || 48);

var Minimap = {};
Minimap.passageCacheCountMax = 5;
Minimap.regex = {
    wallEvent: /<(?:ミニマップ|MINIMAP)\s*[:\s]\s*(?:壁|障害物|WALL)>/i,
    moveEvent: /<(?:ミニマップ|MINIMAP)\s*[:\s]\s*(?:移動|MOVE)>/i,
    person:    /<(?:ミニマップ|MINIMAP)\s*[:\s]\s*(?:人物|PERSON)\s*(\d+)>/i,
    object:    /<(?:ミニマップ|MINIMAP)\s*[:\s]\s*OBJ(?:ECT)?\s*(\d+)>/i
};
Minimap.keysRequireNumber = [ 'person', 'object' ];
Minimap.dirFlags = { down: 0x01, left: 0x02, right: 0x04, up: 0x08 };
Minimap.maskStyles = { none: 0, ellipse: 1, roundedRect: 2, hex1: 3, hex2: 4 };

/**
 * 指定した円に内接する正 n 角形の塗り潰し
 */
var fillRegularPolygon = function(graphics, x, y, width, height, startRad, vertexCount)
{
    if (vertexCount < 1)
    {
        return;
    }

    var points = [];
    for (var i = 0; i < vertexCount; i++)
    {
        var angle = i * Math.PI * 2 / vertexCount - Math.PI / 2 + startRad;
        points.push(x + Math.cos(angle) * width);
        points.push(y + Math.sin(angle) * height);
    }

    graphics.drawPolygon(points);
};

//-----------------------------------------------------------------------------
// Plugin commands

var _KMS_Game_Interpreter_pluginCommand =
        Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args)
{
    _KMS_Game_Interpreter_pluginCommand.call(this, command, args);

    if (command !== 'Minimap')
    {
        return;
    }

    switch (args[0])
    {
    case 'show':
        $gameSystem.setMinimapEnabled(true);
        break;
    case 'hide':
        $gameSystem.setMinimapEnabled(false);
        break;
    case 'refresh':
        $gameMap.refreshMinimapCache();
        break;
    default:
        // 不明なコマンド
        console.log('[Minimap %1] Unknown command.'.format(args[0]));
        break;
    }
};

//-----------------------------------------------------------------------------
// Tilemap

Tilemap.prototype.getTileCount = function()
{
    return {
        x: Math.ceil(this._width  / this._tileWidth)  + 1,
        y: Math.ceil(this._height / this._tileHeight) + 1
    };
};

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

var _KMS_Game_Temp_initialize = Game_Temp.prototype.initialize;
Game_Temp.prototype.initialize = function()
{
    _KMS_Game_Temp_initialize.call(this);

    this._minimapPassageCache     = [];
    this._minimapCacheRefreshFlag = false;
};

Object.defineProperty(Game_Temp.prototype, 'minimapCacheRefreshFlag', {
    get: function()
    {
        return this._minimapCacheRefreshFlag;
    },
    set: function(value)
    {
        this._minimapCacheRefreshFlag = value;
    },
    configurable: true
});

Game_Temp.prototype.getMinimapPassageCache = function(mapId)
{
    return null;
};

/**
 * 通行可否キャッシュの登録
 */
Game_Temp.prototype.registerMinimapPassageCache = function(mapId, passage)
{
    // 最新のキャッシュは先頭に置く
    var newCache = [ { mapId: mapId, passage: passage } ].concat(
        this._minimapPassageCache.filter(function(cache)
        {
            return cache.mapId !== mapId;
        }));

    // しばらく参照されていないキャッシュは消す
    this._minimapPassageCache = newCache.slice(0, Minimap.passageCacheCountMax);
};

/**
 * 通行可否キャッシュの取得
 */
Game_Temp.prototype.getMinimapPassageCache = function(mapId)
{
    for (var i = 0; i < this._minimapPassageCache.length; i++)
    {
        var tempCache = this._minimapPassageCache[i];
        if (tempCache.mapId !== mapId)
        {
            continue;
        }

        if (i > 0)
        {
            // 取得したキャッシュは先頭に移す
            this.registerMinimapPassageCache(mapId, tempCache);

        }
        return tempCache;
    }

    return null;
};

/**
 * 通行可否キャッシュをクリア
 */
Game_Temp.prototype.clearMinimapPassageCache = function(mapId)
{
    this._minimapPassageCache     = [];
    this._minimapCacheRefreshFlag = true;
};

//-----------------------------------------------------------------------------
// Game_System

/**
 * ミニマップの有効状態を取得
 */
Game_System.prototype.isMinimapEnabled = function()
{
    return this._minimapEnabled != null ? this._minimapEnabled : true;
};

/**
 * ミニマップの有効状態を設定
 */
Game_System.prototype.setMinimapEnabled = function(enabled)
{
    this._minimapEnabled = !!enabled;
};

//-----------------------------------------------------------------------------
// Game_Map

var _KMS_Game_Map_setup = Game_Map.prototype.setup;
Game_Map.prototype.setup = function(mapId)
{
    _KMS_Game_Map_setup.call(this, mapId);

    this.setupMinimap();
};

/**
 * ミニマップに関する設定
 */
Game_Map.prototype.setupMinimap = function()
{
    this._minimapEnabled = !$dataMap.meta.NoMinimap;
};

/**
 * ミニマップの表示可否を取得
 */
Game_Map.prototype.isMinimapEnabled = function()
{
    return this._minimapEnabled;
};

/**
 * ミニマップのキャッシュを再構築
 */
Game_Map.prototype.refreshMinimapCache = function()
{
    $gameTemp.clearMinimapPassageCache();
};

//-----------------------------------------------------------------------------
// Game_Character

Game_Character.prototype.isMinimapAttributeDirty = function()
{
    // 何もしない
};

Game_Character.prototype.setMinimapAttributeDirty = function()
{
    // 何もしない
};

Game_Character.prototype.clearMinimapAttributeDirty = function()
{
    // 何もしない
};

//-----------------------------------------------------------------------------
// Game_Event

var _KMS_Game_Event_setupPage = Game_Event.prototype.setupPage;
Game_Event.prototype.setupPage = function()
{
    _KMS_Game_Event_setupPage.call(this);

    this.setupMinimapAttribute();
};

/**
 * ミニマップ用属性の設定
 */
Game_Event.prototype.setupMinimapAttribute = function()
{
    this._minimapAttribute = { dirty: true, wall: false, move: false, person: -1, object: -1 };

    var isComment = function(command)
    {
        return command && (command.code === 108 || command.code === 408);
    };

    // 注釈以外に達するまで解析
    var page = this.page();
    if (!page)
    {
        return;
    }

    var commands = page.list;
    var index    = 0;
    var command  = commands[index++];
    while (isComment(command))
    {
        var comment = command.parameters[0];

        this._minimapAttribute.wall |= Minimap.regex.wallEvent.test(comment);
        this._minimapAttribute.move |= Minimap.regex.moveEvent.test(comment);
        Minimap.keysRequireNumber.forEach(function(key)
        {
            var match = Minimap.regex[key].exec(comment);
            if (match)
            {
                this._minimapAttribute[key] = Number(match[1]);
            }
        }, this);

        command = commands[index++];
    }
};

Game_Event.prototype.isMinimapAttributeDirty = function()
{
    return this._minimapAttribute.dirty;
};

Game_Event.prototype.setMinimapAttributeDirty = function()
{
    this._minimapAttribute.dirty = true;
};

Game_Event.prototype.clearMinimapAttributeDirty = function()
{
    this._minimapAttribute.dirty = false;
};

Game_Event.prototype.isMinimapWall = function()
{
    return this._minimapAttribute.wall;
};

Game_Event.prototype.isMinimapMove = function()
{
    return this._minimapAttribute.move;
};

Game_Event.prototype.minimapPersonType = function()
{
    return this._minimapAttribute.person;
};

Game_Event.prototype.minimapObjectType = function()
{
    return this._minimapAttribute.object;
};

//-----------------------------------------------------------------------------
// Game_Vehicle

/**
 * 現在のマップに存在するか
 */
Game_Vehicle.prototype.isInCurrentMap = function()
{
    return this._mapId === $gameMap.mapId();
};

//-----------------------------------------------------------------------------
// Game_MinimapPassageCache

function Game_MinimapPassageCache()
{
    this.initialize.apply(this, arguments);
};

Game_MinimapPassageCache.prototype.initialize = function(map, grid)
{
    this._mapId = map.mapId();
    this._width = map.width();
    this._height = map.height();
    this._grid = grid;
    this._blockCount = {
        x: Math.floor((this._width  + grid.x - 1) / grid.x),
        y: Math.floor((this._height + grid.y - 1) / grid.y)
    };

    // 各タイルの通行可否フラグ
    this._flags = new Array(this._width * this._height);

    // チェック済みブロックフラグ
    this._blockChecked = new Array(this._blockCount.x * this._blockCount.y);
};

/**
 * ブロック数の取得
 */
Game_MinimapPassageCache.prototype.getBlockCount = function()
{
    return this._blockCount;
};

/**
 * ブロックチェック済み判定
 */
Game_MinimapPassageCache.prototype.isBlockChecked = function(bx, by)
{
    return this._blockChecked[bx + by * this._blockCount.y];
};

/**
 * ブロックチェック済みフラグの設定
 */
Game_MinimapPassageCache.prototype.setBlockChecked = function(bx, by, checked)
{
    this._blockChecked[bx + by * this._blockCount.y] = checked;
};

/**
 * 通行フラグの取得
 */
Game_MinimapPassageCache.prototype.getFlag = function(x, y)
{
    if ($gameMap.isLoopHorizontal())
    {
        x = (x + this._width) % this._width;
    }
    else if (x < 0 || x >= this._width)
    {
        return 0;
    }

    if ($gameMap.isLoopVertical())
    {
        y = (y + this._height) % this._height;
    }
    else if (y < 0 || y >= this._height)
    {
        return 0;
    }

    return this._flags[x + y * this._width];
};

/**
 * 通行フラグの設定
 */
Game_MinimapPassageCache.prototype.setFlag = function(x, y, flag)
{
    this._flags[x + y * this._width] = flag;
};

/**
 * 指定位置が通行可能か
 */
Game_MinimapPassageCache.prototype.isPassable = function(x, y)
{
    return this.getFlag(x, y) !== 0;
};

/**
 * 指定方向への通行が可能か
 */
Game_MinimapPassageCache.prototype.isPassableDir = function(x, y, dir)
{
    var flag = this.getFlag(x, y);
    switch (dir)
    {
        case 2: return (flag & Minimap.dirFlags.down)  !== 0;
        case 4: return (flag & Minimap.dirFlags.left)  !== 0;
        case 6: return (flag & Minimap.dirFlags.right) !== 0;
        case 8: return (flag & Minimap.dirFlags.up)    !== 0;
        default: return false;
    }
};

//-----------------------------------------------------------------------------
// Sprite_Minimap
//
// ミニマップ用のスプライトです。

function Sprite_Minimap()
{
    this.initialize.apply(this, arguments);
}

Sprite_Minimap.prototype = Object.create(Sprite_Base.prototype);
Sprite_Minimap.prototype.constructor = Sprite_Minimap;

Sprite_Minimap.prototype.initialize = function()
{
    Sprite_Base.prototype.initialize.call(this);

    var rect = Params.mapRect;
    this._gridNumber = {
        x: Math.floor((rect.width + Params.gridSize - 1) / Params.gridSize),
        y: Math.floor((rect.height + Params.gridSize - 1) / Params.gridSize),
    };
    this._drawGridNumber = { x: this._gridNumber.x + 2, y: this._gridNumber.y + 2 };
    this._drawRange = { begin: new PIXI.Point(0, 0), end: new PIXI.Point(0, 0) };

    this._lastPosition = new Point($gamePlayer.x, $gamePlayer.y);
    this._scrollDiff = new Point();

    this.createSubSprites();

    this._baseOpacity = 255;
    this.setColorTone([-64, -64, 128, 64]);

    this.move(rect.x, rect.y);
};

/**
 * 内部のスプライトを作成
 */
Sprite_Minimap.prototype.createSubSprites = function()
{
    this._bitmapSize = {
        width: (this._gridNumber.x + 2) * Params.gridSize,
        height: (this._gridNumber.y + 2) * Params.gridSize
    };

    this.createBaseSprite();
    this.createPassageSprite();
    this.createObjectSprites();
    this.createPlayerSprite();
};

/**
 * ベース部分のスプライトを作成
 */
Sprite_Minimap.prototype.createBaseSprite = function()
{
    var rect = Params.mapRect;

    this._baseSprite = new Sprite();
    this._baseSprite.bitmap = new Bitmap(rect.width, rect.height);
    this._baseSprite.bitmap.fillAll(Params.backgroundColor);

    this.addChild(this._baseSprite);

    if (Params.displayTileImage)
    {
        this._tilemap = Graphics.isWebGL() ?
            new ShaderTilemap() :
            new Tilemap();

        // 軽量化のためにタイルのアニメーションを切る
        this._tilemap.update = null;

        this.addChild(this._tilemap);
    }

    this.applyMask(rect);
};

/**
 * マスクの適用
 */
Sprite_Minimap.prototype.applyMask = function(rect)
{
    this._maskGraphic = new PIXI.Graphics();
    this._maskGraphic.beginFill(0x000000);

    switch (Params.maskStyle)
    {
        case Minimap.maskStyles.ellipse:
            this._maskGraphic.drawEllipse(rect.width / 2, rect.height / 2, rect.width / 2, rect.height / 2);
            break;

        case Minimap.maskStyles.roundedRect:
            this._maskGraphic.drawRoundedRect(0, 0, rect.width, rect.height, 64);
            break;

        case Minimap.maskStyles.hex1:
        case Minimap.maskStyles.hex2:
            fillRegularPolygon(
                this._maskGraphic,
                rect.width / 2,
                rect.height / 2,
                rect.width / 2,
                rect.height / 2,
                (Params.maskStyle === Minimap.maskStyles.hex1) ? 0 : (Math.PI / 2),
                6);
            break;

        default:
            this._maskGraphic.drawRect(0, 0, rect.width, rect.height);
            break;
    }

    this._maskGraphic.endFill();

    this.addChild(this._maskGraphic);
    this.mask = this._maskGraphic;
};

/**
 * 通行可能領域スプライトの作成
 */
Sprite_Minimap.prototype.createPassageSprite = function()
{
    this._passageSprite = new Sprite_MinimapPassage(this._bitmapSize.width, this._bitmapSize.height);

    this.updateScroll();

    this.addChild(this._passageSprite);
};

/**
 * オブジェクトスプライトの作成
 */
Sprite_Minimap.prototype.createObjectSprites = function()
{
    this._objectSprites = [];

    var objects = $gameMap.events().concat($gameMap.vehicles());
    objects.forEach(function(obj)
    {
        var sprite = new Sprite_MinimapIcon();
        sprite.setObject(obj);

        this._objectSprites.push(sprite);
        this.addChild(sprite);
    }, this);
};

/**
 * 現在位置スプライトの作成
 */
Sprite_Minimap.prototype.createPlayerSprite = function()
{
    var rect = Params.mapRect;

    this._playerSprite = new Sprite_MinimapIcon();
    this._playerSprite.bitmap = ImageManager.loadSystem(Params.playerIconImage);
    this._playerSprite.x = rect.width / 2;
    this._playerSprite.y = rect.height / 2;

    this.addChild(this._playerSprite);
};

/**
 * 全体の不透明度を設定
 */
Sprite_Minimap.prototype.setWholeOpacity = function(baseOpacity)
{
    this.opacity = this._baseOpacity * baseOpacity / 255;
};

/**
 * ミニマップ位置の移動
 */
Sprite_Minimap.prototype.move = function(x, y)
{
    this.x = x;
    this.y = y;
};

Sprite_Minimap.prototype.isNeedUpdate = function()
{
    return this.visible && this.opacity > 0;
};

Sprite_Minimap.prototype.update = function()
{
    Sprite_Base.prototype.update.call(this);

    this.updateVisibility();

    if ($gameTemp.minimapCacheRefreshFlag)
    {
        $gameTemp.minimapCacheRefreshFlag = false;
        this.refreshPassageTable();
    }

    if (!this.isNeedUpdate())
    {
        return;
    }

    this.updatePosition();
    this.updateTilemap();
    this.updatePassageSprite();
    this.updateObjectSprites();
    this.updatePlayerSprite();
};

/**
 * 可視状態の更新
 */
Sprite_Minimap.prototype.updateVisibility = function()
{
    this.visible =
        $gameSystem.isMinimapEnabled() &&
        $gameMap.isMinimapEnabled();
};

/**
 * 描画範囲の更新
 */
Sprite_Minimap.prototype.updateDrawRange = function()
{
    var range = {
        x: Math.floor(this._drawGridNumber.x / 2),
        y: Math.floor(this._drawGridNumber.y / 2)
    };
    this._drawRange.begin.x = $gamePlayer.x - range.x;
    this._drawRange.begin.y = $gamePlayer.y - range.y;
    this._drawRange.end.x   = $gamePlayer.x + range.x;
    this._drawRange.end.y   = $gamePlayer.y + range.y;
};

/**
 * タイルマップの表示領域を更新
 */
Sprite_Minimap.prototype.updateTilemap = function()
{
    if (!this._tilemap)
    {
        return;
    }

    this._tilemap.origin.x =
        ($gamePlayer._realX - this._tileCount.x / 2.0 + 1.5) * $gameMap.tileWidth();
    this._tilemap.origin.y =
        ($gamePlayer._realY - this._tileCount.y / 2.0 + 1.5) * $gameMap.tileHeight();
};

/**
 * 通行可能領域スプライトの更新
 */
Sprite_Minimap.prototype.updatePassageSprite = function()
{
    // TODO: 実装
};

/**
 * オブジェクトスプライトの更新
 */
Sprite_Minimap.prototype.updateObjectSprites = function()
{
    this._objectSprites.forEach(function(sprite)
    {
        var obj = sprite.getObject();
        if (!obj || !this.isInDrawRange(obj.x - 1, obj.y - 1))
        {
            sprite.visible = false;
            return;
        }

        sprite.updatePosition(this._drawRange.begin, this._scrollDiff);
    }, this);
};

/**
 * 現在位置スプライトの更新
 */
Sprite_Minimap.prototype.updatePlayerSprite = function()
{
    // スプライトの向きを設定
    var angle;
    if (KMS.imported['3DVehicle'] && $gameMap.is3DMode())
    {
        angle = -$gameMap.get3DPlayerAngle();
    }
    else
    {
        switch ($gamePlayer.direction())
        {
            case 2:  angle = Math.PI; break;
            case 4:  angle = Math.PI * 3 / 2; break;
            case 6:  angle = Math.PI / 2; break;
            case 8:  angle = 0; break;
            default: angle = 0; break;
        }
    }
    this._playerSprite.rotation = angle;
};

/**
 * ミニマップの再構築
 */
Sprite_Minimap.prototype.refresh = function()
{
    this.updatePosition();
    this.refreshTilemap();
    this.refreshParameters();
    this.refreshMapImage();
};

/**
 * タイルマップの再構築
 */
Sprite_Minimap.prototype.refreshTilemap = function()
{
    if (!this._tilemap)
    {
        return;
    }

    var rect = Params.mapRect;
    var baseSize = {
        width:  Math.floor(rect.width  * $gameMap.tileWidth()  / Params.gridSize),
        height: Math.floor(rect.height * $gameMap.tileHeight() / Params.gridSize)
    };

    // タイルマップのパラメータを設定
    this._tilemap.tileWidth  = $gameMap.tileWidth();
    this._tilemap.tileHeight = $gameMap.tileHeight();
    this._tilemap.width      = baseSize.width;
    this._tilemap.height     = baseSize.height;
    this._tilemap.setData($gameMap.width(), $gameMap.height(), $gameMap.data());
    this._tilemap.horizontalWrap = $gameMap.isLoopHorizontal();
    this._tilemap.verticalWrap   = $gameMap.isLoopVertical();

    var scaleX = Params.mapRect.width / this._tilemap.width;
    var scaleY = Params.mapRect.height / this._tilemap.height;
    this._tilemap.scale.x = scaleX;
    this._tilemap.scale.y = scaleY;
    this._tilemap.x += this._tilemap._margin * scaleX;
    this._tilemap.y += this._tilemap._margin * scaleY;
    //this._tilemap.width += this._tilemap._margin * 2;
    //this._tilemap.height += this._tilemap._margin * 2;

    Spriteset_Map.prototype.loadTileset.call(this);

    // タイルマップを再描画するために update を呼ぶ
    (Graphics.isWebGL() ? ShaderTilemap : Tilemap)
        .prototype.update.call(this._tilemap);

    this._tileCount = this._tilemap.getTileCount();
};

/**
 * ミニマップ用パラメータの再構築
 */
Sprite_Minimap.prototype.refreshParameters = function()
{
    this.updateDrawRange();
    this.refreshPassageTable();
};

/**
 * 通行フラグテーブルの更新
 */
Sprite_Minimap.prototype.refreshPassageTable = function()
{
    this._passageCache = this.getPassageTableCache();
};

/**
 * 現在のマップに対応する通行フラグキャッシュを取得
 */
Sprite_Minimap.prototype.getPassageTableCache = function()
{
    return new Game_MinimapPassageCache($gameMap, this._drawGridNumber);
};

/**
 * マップ画像の再構築
 */
Sprite_Minimap.prototype.refreshMapImage = function()
{
    this.drawMap();
};

/**
 * 指定した座標が含まれるブロックの通行フラグをスキャン
 */
Sprite_Minimap.prototype.scanPassage = function(x, y)
{
    var mapWidth  = $gameMap.width();
    var mapHeight = $gameMap.height();
    if ($gameMap.isLoopHorizontal())
    {
        x = (x + mapWidth) % mapWidth;
    }
    if ($gameMap.isLoopVertical())
    {
        y = (y + mapHeight) % mapHeight;
    }

    var blocks = this._passageCache.getBlockCount();
    var bx = Math.floor(x / this._drawGridNumber.x);
    var by = Math.floor(y / this._drawGridNumber.y);

    if (bx < 0 || bx >= blocks.x || by < 0 || by >= blocks.y)
    {
        // マップ範囲外
        return;
    }

    if (this._passageCache.isBlockChecked(bx, by))
    {
        // 探索済み
        return;
    }

    var range = {
        x: {
            begin: bx * this._drawGridNumber.x,
            end:   (bx + 1) * this._drawGridNumber.x
        },
        y: {
            begin: by * this._drawGridNumber.y,
            end:   (by + 1) * this._drawGridNumber.y
        }
    };

    // 探索範囲内の通行テーブルを生成
    for (var ty = range.y.begin; ty < range.y.end; ty++)
    {
        if (ty < 0 || ty >= mapHeight)
        {
            continue;
        }

        for (var tx = range.x.begin; tx < range.x.end; tx++)
        {
            if (tx < 0 || tx >= mapWidth)
            {
                continue;
            }

            // 通行方向フラグの作成
            // (方向は 2, 4, 6, 8)
            var flag = 0;
            for (var i = 0; i < 4; i++)
            {
                var dir = (i + 1) * 2;
                if ($gameMap.isPassable(tx, ty, dir))
                {
                    flag |= 1 << (dir / 2 - 1);
                }
            }
            this._passageCache.setFlag(tx, ty, flag);
        }
    }

    this._passageCache.setBlockChecked(bx, by, true);
};

/**
 * プレイヤー周囲の通行フラグテーブルを更新
 */
Sprite_Minimap.prototype.updateAroundPassageTable = function()
{
    var gx = this._drawGridNumber.x;
    var gy = this._drawGridNumber.y;
    var dx = $gamePlayer.x - Math.floor(gx / 2);
    var dy = $gamePlayer.y - Math.floor(gy / 2);
    this.scanPassage(dx, dy);
    this.scanPassage(dx + gx, dy);
    this.scanPassage(dx, dy + gy);
    this.scanPassage(dx + gx, dy + gy);
};

/**
 * プレイヤー位置の更新
 */
Sprite_Minimap.prototype.updatePosition = function()
{
    this._scrollDiff.x = ($gamePlayer._realX - $gamePlayer.x) * Params.gridSize;
    this._scrollDiff.y = ($gamePlayer._realY - $gamePlayer.y) * Params.gridSize;
    this.updateScroll();

    if (this._lastPosition.x !== $gamePlayer.x ||
        this._lastPosition.y !== $gamePlayer.y)
    {
        this.updateDrawRange();
        this.drawMap();
        this._lastPosition.x = $gamePlayer.x;
        this._lastPosition.y = $gamePlayer.y;
    }
};

/**
 * スクロール処理
 */
Sprite_Minimap.prototype.updateScroll = function()
{
    var offset = {
        x: Math.floor((Params.gridSize) * 1.5) + this._scrollDiff.x,
        y: Math.floor((Params.gridSize) * 1.5) + this._scrollDiff.y
    };
    var rect = Params.mapRect;
    this._passageSprite.setFrame(offset.x, offset.y, rect.width, rect.height);
};

/**
 * 描画範囲か判定
 */
Sprite_Minimap.prototype.isInDrawRange = function(x, y)
{
    var begin = this._drawRange.begin;
    var end   = this._drawRange.end;

    var dx = x;
    if ($gameMap.isLoopHorizontal())
    {
        dx = (dx + $gameMap.width()) % $gameMap.width();
    }

    var dy = y;
    if ($gameMap.isLoopVertical())
    {
        dy = (dy + $gameMap.height()) % $gameMap.height();
    }

    return x >= begin.x && x <= end.x &&
        y >= begin.y && y <= end.y;
};

/**
 * マップの範囲内か判定
 */
Sprite_Minimap.prototype.isInMapRange = function(x, y)
{
    return x >= 0 && x < $gameMap.width() &&
        y >= 0 && y < $gameMap.height();
};

/**
 * マップ画像の描画
 */
Sprite_Minimap.prototype.drawMap = function()
{
    this.updateAroundPassageTable();

    var bitmap = this._passageSprite.bitmap;
    bitmap.clear();
    this.drawMapForeground(bitmap);
};

/**
 * 移動可能領域の描画
 */
Sprite_Minimap.prototype.drawMapForeground = function(bitmap)
{
    for (var y = this._drawRange.begin.y; y < this._drawRange.end.y; y++)
    {
        for (var x = this._drawRange.begin.x; x < this._drawRange.end.x; x++)
        {
            this.drawMapForegroundGrid(bitmap, x, y);
        }
    }
};

/**
 * 移動可能グリッドの描画
 */
Sprite_Minimap.prototype.drawMapForegroundGrid = function(bitmap, x, y)
{
    var passage = this._passageCache;
    if (!passage.isPassable(x, y))
    {
        return;
    }

    var dx = (x - this._drawRange.begin.x) * Params.gridSize;
    var dy = (y - this._drawRange.begin.y) * Params.gridSize;
    var dw = Params.gridSize;
    var dh = Params.gridSize;

    if (!passage.isPassableDir(x, y, 2))  // 下方向移動不可
    {
        dh -= 1;
    }
    if (!passage.isPassableDir(x, y, 4))  // 左方向移動不可
    {
        dx += 1;
        dw -= 1;
    }
    if (!passage.isPassableDir(x, y, 6))  // 右方向移動不可
    {
        dw -= 1;
    }
    if (!passage.isPassableDir(x, y, 8))  // 上方向移動不可
    {
        dy += 1;
        dh -= 1;
    }
    bitmap.fillRect(dx, dy, dw, dh, Params.foregroundColor);
};

//-----------------------------------------------------------------------------
// Sprite_MinimapPassage
//
// ミニマップ上の通行可能領域表示用スプライトです。

function Sprite_MinimapPassage()
{
    this.initialize.apply(this, arguments);
}

Sprite_MinimapPassage.prototype = Object.create(Sprite.prototype);
Sprite_MinimapPassage.prototype.constructor = Sprite_MinimapPassage;

Sprite_MinimapPassage.prototype.initialize = function(width, height)
{
    Sprite.prototype.initialize.call(this);

    this.bitmap = new Bitmap(width, height);
};

//-----------------------------------------------------------------------------
// Sprite_MinimapIcon
//
// ミニマップ上のアイコン用スプライトです。

function Sprite_MinimapIcon()
{
    this.initialize.apply(this, arguments);
}

Sprite_MinimapIcon.prototype = Object.create(Sprite.prototype);
Sprite_MinimapIcon.prototype.constructor = Sprite_MinimapIcon;

Sprite_MinimapIcon.prototype.initialize = function()
{
    Sprite.prototype.initialize.call(this);

    this.bitmap   = ImageManager.loadSystem(Params.objectIconImage);
    this.anchor.x = 0.5;
    this.anchor.y = 0.5;

    this._object        = null;
    this._lastIconIndex = -1;
    this._blinkDuration = 0;
};

/**
 * 表示準備ができているか
 */
Sprite_MinimapIcon.prototype.isReady = function()
{
    return this.bitmap && this.bitmap.isReady();
};

/**
 * 追従するオブジェクトの取得
 */
Sprite_MinimapIcon.prototype.getObject = function()
{
    return this._object;
};

/**
 * 追従するオブジェクトの設定
 */
Sprite_MinimapIcon.prototype.setObject = function(object)
{
    this._object = object;
    if (object)
    {
        this.bitmap = ImageManager.loadSystem(Params.objectIconImage);
        this.updateIconInfo();
    }

    this.refresh();
};

/**
 * 表示位置の更新
 *
 * @param {Point} begin      - ミニマップの表示開始座標
 * @param {Point} scrollDiff - 位置補正のためのマップスクロール量
 */
Sprite_MinimapIcon.prototype.updatePosition = function(begin, scrollDiff)
{
    var x = (this._object.x - begin.x - 1) * Params.gridSize;
    var y = (this._object.y - begin.y - 1) * Params.gridSize;
    this.x = x - scrollDiff.x;
    this.y = y - scrollDiff.y;

    if (KMS.imported['AreaEvent'])
    {
        // 表示サイズに応じた位置補正
        this.x += this._iconSize * (this.scale.x - 1.0) / 2;
        this.y += this._iconSize * (this.scale.y - 1.0) / 2;
    }
};

/**
 * アイコン情報の更新
 */
Sprite_MinimapIcon.prototype.updateIconInfo = function()
{
    if (!this.isReady())
    {
        this._iconSize    = null;
        this._iconColumns = null;
        return;
    }

    this._iconSize    = Math.floor(this.bitmap.height / 3);
    this._iconColumns = Math.floor(this.bitmap.width / this._iconSize);
};

/**
 * リフレッシュ
 */
Sprite_MinimapIcon.prototype.refresh = function()
{
    this._lastIconIndex = -1;
};

/**
 * オブジェクト更新
 */
Sprite_MinimapIcon.prototype.update = function()
{
    Sprite.prototype.update.call(this);

    this.updateImage();
    this.updateBlink();
};

/**
 * 表示する画像の更新
 */
Sprite_MinimapIcon.prototype.updateImage = function()
{
    if (!this._object)
    {
        return;
    }

    if (!this._iconColumns)
    {
        this.updateIconInfo();
    }

    var iconIndex = this.getCurrentIconIndex();
    if (iconIndex >= 0)
    {
        if (iconIndex !== this._lastIconIndex || this._object.isMinimapAttributeDirty())
        {
            this.setIconIndex(iconIndex);
        }
    }
    else
    {
        this._lastIconIndex = iconIndex;
        this.visible = false;
    }
};

/**
 * 現在のオブジェクトに対応するオブジェクトアイコン番号の取得
 */
Sprite_MinimapIcon.prototype.getCurrentIconIndex = function()
{
    if (!(this._object && this.isReady()))
    {
        return -1;
    }

    if (this._object instanceof Game_Vehicle)
    {
        return this.getCurrentIconIndexForVehicle();
    }
    else
    {
        return this.getCurrentIconIndexForEvent();
    }
};

/**
 * 現在のイベントに対応するアイコン番号の取得
 */
Sprite_MinimapIcon.prototype.getCurrentIconIndexForEvent = function()
{
    var obj = this._object;

    if (obj.isMinimapMove())
    {
        return 0;
    }
    else if (obj.minimapPersonType() >= 0)
    {
        // person は 2 行目
        return this._iconColumns + obj.minimapPersonType();
    }
    else if (obj.minimapObjectType() >= 0)
    {
        // object は 3 行目
        return this._iconColumns * 2 + obj.minimapObjectType();
    }
    else
    {
        return -1;
    }
};

/**
 * 現在の乗り物に対応するアイコン番号の取得
 */
Sprite_MinimapIcon.prototype.getCurrentIconIndexForVehicle = function()
{
    var obj = this._object;

    if (!obj.isInCurrentMap())
    {
        return -1;
    }
    else if (obj.isBoat())
    {
        return $gamePlayer.isInBoat() ? -1 : 1;
    }
    else if (obj.isShip())
    {
        return $gamePlayer.isInShip() ? -1 : 2;
    }
    else if (obj.isAirship())
    {
        return $gamePlayer.isInAirship() ? -1 : 3;
    }
    else
    {
        // 不明な乗り物
        return -1;
    }
};

/**
 * オブジェクトアイコン番号の設定
 */
Sprite_MinimapIcon.prototype.setIconIndex = function(iconIndex)
{
    this.setFrame(
        (iconIndex % this._iconColumns) * this._iconSize,
        Math.floor(iconIndex / this._iconColumns) * this._iconSize,
        this._iconSize,
        this._iconSize);
    this.updateDisplaySize();

    this._object.clearMinimapAttributeDirty();
    this._lastIconIndex = iconIndex;
    this.visible = true;
};

/**
 * オブジェクトアイコン表示サイズの更新
 */
Sprite_MinimapIcon.prototype.updateDisplaySize = function()
{
    if (!KMS.imported['AreaEvent'] ||
        !(this._object instanceof Game_Event))
    {
        return;
    }

    var area = this._object.getEventTriggerArea();
    this.scale.x = area.width;
    this.scale.y = area.height;
};

/**
 * 明滅エフェクトの更新
 */
Sprite_MinimapIcon.prototype.updateBlink = function()
{
    this._blinkDuration = (this._blinkDuration + 1) % Params.blinkTime;

    var alpha = 128 * (Math.sin(this._blinkDuration * Math.PI * 2 / Params.blinkTime) + 1);
    var color = [255, 255, 255, alpha];
    this.setBlendColor(color);
};

//-----------------------------------------------------------------------------
// Spriteset_Map

var _KMS_Spriteset_Map_createUpperLayer = Spriteset_Map.prototype.createUpperLayer;
Spriteset_Map.prototype.createUpperLayer = function()
{
    _KMS_Spriteset_Map_createUpperLayer.call(this);

    this.createMinimap();
};

/**
 * ミニマップの作成
 */
Spriteset_Map.prototype.createMinimap = function()
{
    this._minimap = new Sprite_Minimap();
    this._minimap.refresh();
    this.addChild(this._minimap);
};

var _KMS_Spriteset_Map = Spriteset_Map.prototype.update;
Spriteset_Map.prototype.update = function()
{
    _KMS_Spriteset_Map.call(this);

    this.updateMinimap();
};

/**
 * ミニマップの更新
 */
Spriteset_Map.prototype.updateMinimap = function()
{
    this._minimap.setWholeOpacity(255 - this._fadeSprite.opacity);
    this._minimap.update();
};

})();