/*:
 * @target MZ
 * @plugindesc 隣接時メッセージ表示
 * @url https://twitter.com/kuroudo119/
 * @url https://github.com/kuroudo119/RPGMZ-Plugin
 * @author kuroudo119 (くろうど)
 * @base DTextPicture
 * @base KRD_MZ_NeighborBalloon
 * 
 * @param MESSAGE_COUNT
 * @text メッセージカウント
 * @desc メッセージを消去するまでのカウントです。1秒60カウント。初期値:100
 * @default 100
 * @type number
 * 
 * @param USE_LENGTH
 * @text 文字数カウント
 * @desc メッセージを消去するまでのカウントに文字数を追加します。1文字あたりのカウントです。初期値:5
 * @default 5
 * @type number
 * 
 * @param FONT_SIZE
 * @text フォントサイズ
 * @desc フォントサイズを変更したい場合に設定します。初期値:0
 * @default 0
 * @type number
 * 
 * @param MINUS_Y
 * @text Y座標補正値
 * @desc ウィンドウの表示位置を上に移動する固定値です。初期値:18
 * @default 18
 * @type number
 * 
 * @command doDTextPicture
 * @text イベントメッセージ表示
 * @desc イベントにメッセージを表示します。自動消去あり。
 * @arg text
 * @text メッセージ
 * @desc 表示するメッセージです。
 * @type multiline_string
 * 
 * @help
# KRD_MZ_NeighborMessage.js

隣接時メッセージ表示

## 権利表記

(c) 2021 kuroudo119 (くろうど)

## 利用規約

このプラグインはMITライセンスです。
https://github.com/kuroudo119/RPGMZ-Plugin/blob/master/LICENSE

## 必要プラグイン

以下のプラグインが必要です。

DTextPicture
KRD_MZ_NeighborBalloon

## 使い方

イベントのメモ欄に msgText, msgZone の2つのタグを書きます。
イベントのメモ欄が狭いことに注意してください。

### msgText

<msgText:"テキスト","テキスト2">

表示する文章をクォーテーション（' または "）で囲んで記述します。
複数ページある場合はカンマ区切りで記述します。

改行したい場合は \n を記述します。

### msgZone

<msgZone:2,1>

メッセージを表示するプレイヤーとイベントの距離を記述します。
複数ページある場合はカンマ区切りで記述します。

省略すると 1 になります。

## プラグインコマンド

### イベントメッセージ表示 (doDTextPicture)

プラグインコマンド実行のタイミングで、
イベントにメッセージを表示します。

タグと同様に自動で消去します。
宝箱の中身を表示する等を想定しています。

## 制約事項

本プラグインでは、動的文字列ピクチャ生成プラグインを利用しています。
そのため、動的文字列ピクチャ生成プラグインの設定に影響します。
本プラグインでは、設定のウィンドウを true にします。

## 更新履歴

- ver.0.0.1 (2024/03/24) 作成開始
- ver.0.1.0 (2024/03/24) 非公開版完成
- ver.1.0.0 (2024/03/24) 公開
- ver.1.0.1 (2024/03/24) ピクチャIDの修正
- ver.1.0.2 (2024/03/25) ヘルプに制約事項を追記
- ver.1.1.0 (2024/03/26) 内部処理を修正
- ver.1.1.1 (2024/03/26) 既存セーブデータに対応
- ver.2.0.0 (2024/03/29) プラグインパラメータなど大きな変更
- ver.2.1.0 (2024/03/31) イベントにメッセージ表示を追加
- ver.2.1.1 (2024/04/11) 隣接時フキダシ表示への競合を修正
- ver.2.2.0 (2024/11/14) プラグインコマンドの複数回実行に対応
- ver.2.2.1 (2024/12/02) 出るかもしれないエラー対応
- ver.2.2.2 (2024/12/16) eraseAllMessage を修正

 * 
 * 
 */

(() => {

"use strict";

const PLUGIN_NAME = document.currentScript.src.match(/^.*\/(.*).js$/)[1];
const PARAM = PluginManager.parameters(PLUGIN_NAME);

const MESSAGE_COUNT = Number(PARAM["MESSAGE_COUNT"]) || 0;
const USE_LENGTH = Number(PARAM["USE_LENGTH"]) || 0;
const FONT_SIZE = Number(PARAM["FONT_SIZE"]) || 0;
const MINUS_Y = Number(PARAM["MINUS_Y"]) || 0;

const ADD_PICTURE_ID = 20;

const TAG_TEXT = "msgText";
const TAG_ZONE = "msgZone";

const NAME = "";
const ORIGIN = 1;
const SCALE_X = 100;
const SCALE_Y = 100;
const OPACITY = 255;
const BLEND_MODE = 0;
const DURATION = 1;
const EASING_TYPE = 0;

const D_TEXT_SETTING = {window: true};

//--------------------------------------

PluginManager.registerCommand(PLUGIN_NAME, "doDTextPicture", function(args) {
	this.doDTextPicture(args.text);
});

//--------------------------------------

const _Game_Event_updateStop = Game_Event.prototype.updateStop;
Game_Event.prototype.updateStop = function() {
	_Game_Event_updateStop.call(this, ...arguments);
	this.doNeighborMessage();
};

Game_Event.prototype.doNeighborMessage = function() {
	if (this._pageIndex < 0) {
		return;
	}

	const tagText = this.event().meta[TAG_TEXT];
	const tagZone = this.event().meta[TAG_ZONE];
	const textPage = tagText ? JSON.parse("[" + tagText + "]") : null;
	const zonePage = tagZone ? JSON.parse("[" + tagZone + "]") : null;
	const text = textPage ? textPage[this._pageIndex] : "";
	const zone = zonePage ? parseInt(zonePage[this._pageIndex]) : 1;

	this.doMessage(text, zone);
};

Game_Event.prototype.doMessage = function(text, zone = 1) {
	this._oldPositionMsg = this._oldPositionMsg ? this._oldPositionMsg : false;
	const newPosition = this.playerIsInZone(zone);
	if (text && this._msgPictureId == null && newPosition && this._oldPositionMsg !== newPosition) {
		this.doDTextPicture(text);
	}
	this._oldPositionMsg = newPosition;
};

Game_Event.prototype.doDTextPicture = function(text) {
	const convText = Window_Base.prototype.convertEscapeCharacters(text);
	const cutRubyText = cutRuby(convText);
	const plusCount = cutRubyText.toString().length * USE_LENGTH;
	const minusY = this.minusY(cutRubyText);

	// DTextPicture プラグインの設定をする。
	$gameScreen.setDTextPicture(convText, FONT_SIZE);
	$gameScreen.setDtextSetting(D_TEXT_SETTING);

	const msgPicture = {
		pictureId: $gameScreen.getMsgPictureId(),
		pictureCount: MESSAGE_COUNT + plusCount,
		x: this.screenX(),
		minusY: minusY,
		y: this.screenY() - minusY,
	};

	this._msgPictureList = this._msgPictureList ? this._msgPictureList : [];
	this._msgPictureList.push(msgPicture);

	$gameScreen.showPicture(msgPicture.pictureId, NAME, ORIGIN, msgPicture.x, msgPicture.y, SCALE_X, SCALE_Y, OPACITY, BLEND_MODE);
};

Game_Event.prototype.minusY = function(text) {
	const line = text.toString().split("\n").length;
	const fontSize = FONT_SIZE || $gameSystem.mainFontSize();
	const msgheight = line * fontSize;

	const sprites = SceneManager._scene._spriteset._characterSprites;
	// 同じ _characterName が複数ある場合に、最初のものを取得するが、
	// 同じ画像なので、同じ height であるはず。
	const sprite = sprites.find(sprite => sprite._characterName === this._characterName);
	const eventHeight = sprite._frame.height;

	return eventHeight + msgheight + MINUS_Y;
};

function cutRuby(text) {
	return typeof KRD_RUBY !== "undefined" ? KRD_RUBY.cutRuby(text) : text;
}

Game_Event.prototype.moveMessage = function(msgPicture) {
	if (msgPicture) {
		const x = this.screenX();
		const y = this.screenY() - msgPicture.minusY;
		$gameScreen.movePicture(msgPicture.pictureId, ORIGIN, x, y, SCALE_X, SCALE_Y, OPACITY, BLEND_MODE, DURATION, EASING_TYPE);
	}
};

Game_Event.prototype.eraseMessage = function(msgPicture) {
	if (msgPicture) {
		$gameScreen.erasePicture(msgPicture.pictureId);
		$gameScreen.setMsgPictureId(msgPicture.pictureId);
		msgPicture.pictureId = null;
	}
};

const _Game_Event_update = Game_Event.prototype.update;
Game_Event.prototype.update = function() {
	_Game_Event_update.call(this, ...arguments);
	this.updatePictureCountAll();
};

Game_Event.prototype.updatePictureCountAll = function() {
	if (this._msgPictureList) {
		for (const msgPicture of this._msgPictureList) {
			this.updatePictureCount(msgPicture);
		}

		this._msgPictureList = this._msgPictureList.filter(msgPicture => msgPicture.pictureCount != null);
	}
};

Game_Event.prototype.updatePictureCount = function(msgPicture) {
	if (msgPicture.pictureCount != null) {
		if (msgPicture.pictureCount > 0) {
			this.moveMessage(msgPicture);
			msgPicture.pictureCount--;
		} else if (msgPicture.pictureCount <= 0) {
			this.eraseMessage(msgPicture);
			msgPicture.pictureCount = null;
		}
	}
};

//--------------------------------------

const _Game_Screen_clear = Game_Screen.prototype.clear;
Game_Screen.prototype.clear = function() {
	_Game_Screen_clear.call(this, ...arguments);
	this.initMsgPictureIdList();
};

Game_Screen.prototype.initMsgPictureIdList = function() {
	const maxId = this.maxPictures();
	this._msgPictureIdList = [];
	this._msgPictureIdListMaster = [];
	for (let i = 0; i < ADD_PICTURE_ID; i++) {
		this._msgPictureIdList.push(maxId - i);
		this._msgPictureIdListMaster.push(maxId - i);
	}
};

Game_Screen.prototype.getMsgPictureId = function() {
	if (!this._msgPictureIdList) {
		this.initMsgPictureIdList();
	}
	return this._msgPictureIdList.pop();
};

Game_Screen.prototype.setMsgPictureId = function(id) {
	if (id != null) {
		this._msgPictureIdList.push(id);
	}
};

const _Game_Screen_maxPictures = Game_Screen.prototype.maxPictures;
Game_Screen.prototype.maxPictures = function() {
	return _Game_Screen_maxPictures.call(this, ...arguments) + ADD_PICTURE_ID;
};

//--------------------------------------

Game_Map.prototype.eraseAllMessage = function() {
	$gameScreen._msgPictureIdListMaster.forEach(pid => $gameScreen.erasePicture(pid));
};

const _Scene_Map_terminate = Scene_Map.prototype.terminate;
Scene_Map.prototype.terminate = function() {
	$gameMap.eraseAllMessage();
	_Scene_Map_terminate.call(this, ...arguments);
};

//--------------------------------------
// イベントにピクチャ付与

Game_Interpreter.prototype.doDTextPicture = function(text) {
	const event = this.character();
	if (event) {
		event.doDTextPicture(text);
	}
};

//--------------------------------------
})();
