// ShowItemIcon.js Ver.3.0.0
// MIT License (C) 2022 あわやまたな
// http://opensource.org/licenses/mit-license.php

/*:
* @target MV MZ
* @plugindesc  キャラクターの頭上にアイテムアイコンを表示できます。
* @author あわやまたな (Awaya_Matana)
* @url https://awaya3ji.seesaa.net/article/485335701.html
* @help［プラグインコマンド（MV）］
* showItemIcon イベントID アイコン番号
* アイコン番号が未入力、または0の時にアイコンを消去できます。
*
* showItemIcon イベントID 番号 種類
* 種類にitem, weapon, armor, skill, stateいずれかを入力する事で
* アイテム、武器、防具、スキル、ステートいずれかのアイコンを直接指定できます。
*
* ［スクリプト］
* 【イベントエディター】
* this.character(イベントID).setIcon(アイコン番号);
* アイコン番号が未入力、または0の時にアイコンを消去できます。
*
* this.character(イベントID).setIcon(番号, "種類");
* 種類にitem, weapon, armor, skill, stateいずれかを入力する事で
* アイテム、武器、防具、スキル、ステートいずれかのアイコンを直接指定できます。
*
* 【移動ルートの設定】
* this.setIcon(アイコン番号);
* アイコン番号が未入力、または0の時にアイコンを消去できます。
*
* this.setIcon(番号, "種類");
* 種類にitem, weapon, armor, skill, stateいずれかを入力する事で
* アイテム、武器、防具、スキル、ステートいずれかのアイコンを直接指定できます。
*
*［オプション］
* イベントの一行目に注釈を置き、以下の形式で入力します。
* するとページ切り替え時に最初からアイコンが表示されます。
* <itemIcon:50> //アイコン番号50のアイコンを表示する。
* <itemIcon:120> //アイコン番号120のアイコンを表示する。
* <itemIcon:1,weapon> //武器1のアイコンを表示する。
* <itemIcon:15,防具> //防具15のアイコンを表示する。
*
*［Z座標］
* 0：下層タイル
* 1：下層キャラクター
* 3：通常キャラクター
* 4：上層タイル
* 5：上層キャラクター
* 6：飛行船の影
* 7：フキダシアイコン
* 8：アニメーション
* 9：タップエフェクト
*
* [更新履歴]
* 2022/01/24：Ver.1.0.0　公開
* 2022/01/24：Ver.1.0.1　イベントの一時消去時にアイコンが残留する不具合を修正。
* 2022/01/24：Ver.1.0.2　スクリプトで入力しやすいように関数を追加。
* 2022/01/24：Ver.1.0.3　アイコンの寸法取得方法を変更。
* 2022/12/09：Ver.2.0.0　PluginCommonBaseの判別方法を修正。ページ別設定をメモから注釈に変更。
* 2022/12/20：Ver.2.0.1　MZのv1.6.0の新機能に対応。
* 2023/02/24：Ver.3.0.0　アイテム、武器、防具、スキル、ステートアイコンの直接指定に対応。
*
* @command showItemIcon
* @text アイテムアイコンの表示
* @desc キャラクターの頭上に、指定したアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg itemIcon
* @text アイコン番号
* @desc 表示するアイテムアイコンの番号です。
* 未入力、または0で消去。
* @default 
* @type icon
*
* @command showItem
* @text アイテムの表示
* @desc キャラクターの頭上に、指定したアイテムのアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg item
* @text アイテムID
* @desc 表示するアイテムのIDです。
* 未入力、または0で消去。
* @default 
* @type item
*
* @command showWeapon
* @text 武器の表示
* @desc キャラクターの頭上に、指定した武器のアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg item
* @text 武器ID
* @desc 表示する武器のIDです。
* 未入力、または0で消去。
* @default 
* @type weapon
*
* @command showArmor
* @text 防具の表示
* @desc キャラクターの頭上に、指定した防具のアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg item
* @text 防具ID
* @desc 表示する防具のIDです。
* 未入力、または0で消去。
* @default 
* @type armor
*
* @command showSkill
* @text スキルの表示
* @desc キャラクターの頭上に、指定したスキルのアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg item
* @text スキルID
* @desc 表示するスキルのIDです。
* 未入力、または0で消去。
* @default 
* @type skill
*
* @command showState
* @text ステートの表示
* @desc キャラクターの頭上に、指定したステートのアイコンを表示します。
*
* @arg character
* @text キャラクター
* @desc 対象とするキャラクターです。
* -1：プレイヤー　0：このイベント
* @default -1
* @type string
*
* @arg item
* @text ステートID
* @desc 表示するステートのIDです。
* 未入力、または0で消去。
* @default 
* @type state
*
* @param iconY
* @text アイコンY座標
* @desc アイコンのY座標を設定値分ずらして表示します。
* @default 0
* @type number
* @min -999999
*
* @param iconZ
* @text アイコンZ座標
* @desc アイコンのZ座標を設定します。
* @default 6.5
* @type string
*
*/

'use strict';
{
	const useMZ = Utils.RPGMAKER_NAME === "MZ";
	const pluginName = document.currentScript.src.match(/^.*\/(.*).js$/)[1];
	const hasPluginCommonBase = typeof PluginManagerEx === "function";
	const parameters = PluginManager.parameters(pluginName);
	const iconY = Number(parameters['iconY']);
	const iconZ = Number(parameters['iconZ']);
	
	if (useMZ) {
		PluginManager.registerCommand(pluginName, "showItemIcon", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.itemIcon);
			}
		});
		PluginManager.registerCommand(pluginName, "showItem", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.item, "item");
			}
		});
		PluginManager.registerCommand(pluginName, "showWeapon", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.item, "weapon");
			}
		});
		PluginManager.registerCommand(pluginName, "showArmor", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.item, "armor");
			}
		});
		PluginManager.registerCommand(pluginName, "showSkill", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.item, "skill");
			}
		});
		PluginManager.registerCommand(pluginName, "showState", function (args) {
			const character = this.character(+args.character);
			if (character) {
				character.setIcon(+args.item, "state");
			}
		});
		if (hasPluginCommonBase) {
			PluginManagerEx.registerCommand(document.currentScript, "showItemIcon", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.itemIcon);
				}
			});
			PluginManagerEx.registerCommand(document.currentScript, "showItem", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.item, "item");
				}
			});
			PluginManagerEx.registerCommand(document.currentScript, "showWeapon", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.item, "weapon");
				}
			});
			PluginManagerEx.registerCommand(document.currentScript, "showArmor", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.item, "armor");
				}
			});
			PluginManagerEx.registerCommand(document.currentScript, "showSkill", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.item, "skill");
				}
			});
			PluginManagerEx.registerCommand(document.currentScript, "showState", function (args) {
				const character = this.character(args.character);
				if (character) {
					character.setIcon(args.item, "state");
				}
			});
		}
	}
	const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
	Game_Interpreter.prototype.pluginCommand = function(command, args) {
		_Game_Interpreter_pluginCommand.apply(this, arguments);
		
		if (command === "showItemIcon") {
			const character = this.character(+args[0]);
			if (character) {
				character.setIcon(+args[1], args[2]);
			}
		}	
		
	};

	//-----------------------------------------------------------------------------
	// Sprite_ItemIcon

	function Sprite_ItemIcon() {
		this.initialize(...arguments);
	}

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

	Sprite_ItemIcon.prototype.initialize = function() {
		Sprite.prototype.initialize.call(this);
		this.initMembers();
		this.loadBitmap();
	};

	Sprite_ItemIcon.prototype.initMembers = function() {
		this._target = null;
		this._itemIconId = 0;
		this.anchor.x = 0.5;
		this.anchor.y = 1;
		this.z = iconZ;
	};

	Sprite_ItemIcon.prototype.loadBitmap = function() {
		this.bitmap = ImageManager.loadSystem("IconSet");
		this.setFrame(0, 0, 0, 0);
	};

	Sprite_ItemIcon.prototype.setup = function(targetSprite, itemIconId) {
		this._target = targetSprite;
		this._itemIconId = itemIconId;
		this.updateFrame();
	};

	Sprite_ItemIcon.prototype.update = function() {
		Sprite.prototype.update.call(this);
		this.updatePosition();
	};

	Sprite_ItemIcon.prototype.updatePosition = function() {
		this.x = this._target.x;
		this.y = this._target.y + iconY - (this._target._character._easyRotating ? this._target.height /2 : this._target.height);
	};

	Sprite_ItemIcon.prototype.updateFrame = function() {
		const w = useMZ ? ImageManager.iconWidth : Window_Base._iconWidth;
		const h = useMZ ? ImageManager.iconHeight : Window_Base._iconHeight;
		const sx = this._itemIconId %16 * w;
		const sy = Math.floor(this._itemIconId /16) * h;
		this.setFrame(sx, sy, w, h);
	};

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

	const _Spriteset_Map_initialize = Spriteset_Map.prototype.initialize;
	Spriteset_Map.prototype.initialize = function() {
		_Spriteset_Map_initialize.call(this);
		if (useMZ) this._itemIconSprites = [];
		this.restoreItemIcons();
	};
	const setItemIcon = useMZ ? "setItemIcon" : "setItemIconMV";
	//マップ復帰時に復元
	Spriteset_Map.prototype.restoreItemIcons = function() {
		for (const sprite of this._characterSprites) {
			const character = sprite._character;
			const itemIconId = character.itemIconId()
			this[setItemIcon]({target:character, itemIconId:itemIconId});
		}
	};
	//MZ
	const _Spriteset_Map_destroy = Spriteset_Map.prototype.destroy;
	Spriteset_Map.prototype.destroy = function(options) {
		this.removeAllItemIcons();
		_Spriteset_Map_destroy.call(this, options);
	};
	//MZ
	const _Spriteset_Map_loadSystemImages = Spriteset_Map.prototype.loadSystemImages;
	Spriteset_Map.prototype.loadSystemImages = function() {
		_Spriteset_Map_loadSystemImages.call(this);
		ImageManager.loadSystem("IconSet");
	};

	const _Spriteset_Map_update = Spriteset_Map.prototype.update;
	Spriteset_Map.prototype.update = function() {
		_Spriteset_Map_update.call(this);
		this.updateItemIcons();
	};

	Spriteset_Map.prototype.updateItemIcons = function() {
		this.processItemIconRequests();
	};

	Spriteset_Map.prototype.processItemIconRequests = function() {
		for (;;) {
			const request = $gameTemp.retrieveItemIcon();
			if (request) {
				this[setItemIcon](request);
			} else {
				break;
			}
		}
	};
	//MZ
	Spriteset_Map.prototype.setItemIcon = function(request) {
		const targetSprite = this.findTargetSprite(request.target);
		if (targetSprite) {
			//アイコンが既に表示されている場合は既存のスプライトに変更を加えるだけで終了
			const existingItemIcon = this.existingItemIcon(request);
			if (existingItemIcon) {
				if (request.itemIconId > 0) {
					existingItemIcon.setup(targetSprite, request.itemIconId);
				} else {
					//アイコン番号が0なら破棄
					this.removeItemIcon(existingItemIcon);
				}
			} else if (request.itemIconId > 0) {
				//アイコン番号が0より大きければスプライトを生成
				const sprite = new Sprite_ItemIcon();
				sprite.targetObject = request.target;
				sprite.setup(targetSprite, request.itemIconId);
				this._effectsContainer.addChild(sprite);
				this._itemIconSprites.push(sprite);
			}
			
		}
	};
	//MZ //既存のアイコンが存在するかどうか
	Spriteset_Map.prototype.existingItemIcon = function(request) {
		for (const sprite of this._itemIconSprites) {
			if (sprite.targetObject === request.target) {
				return sprite;
			}
		}
	};
	//MZ
	Spriteset_Map.prototype.removeItemIcon = function(sprite) {
		this._itemIconSprites.remove(sprite);
		this._effectsContainer.removeChild(sprite);
		sprite.destroy();
	};
	//MZ
	Spriteset_Map.prototype.removeAllItemIcons = function() {
		for (const sprite of this._itemIconSprites.clone()) {
			this.removeItemIcon(sprite);
		}
	};
	//MV
	Spriteset_Map.prototype.setItemIconMV = function(request) {
		const targetSprite = this.findTargetSpriteMV(request.target);
		if (targetSprite) {
			if(request.itemIconId > 0) {
				targetSprite.createItemIconMV();
			} else {
				targetSprite.removeItemIconMV();
			}
		}
	};
	//MV
	Spriteset_Map.prototype.findTargetSpriteMV = function(target) {
		return this._characterSprites.find(sprite => sprite._character === target);
	};

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

	Sprite_Character.prototype.createItemIconMV = function() {
		if (!this._itemIconSprite) this._itemIconSprite = new Sprite_ItemIcon();
		this._itemIconSprite.setup(this, this._character.itemIconId());
		this.parent.addChild(this._itemIconSprite);
	};

	Sprite_Character.prototype.removeItemIconMV = function() {
		if (this._itemIconSprite) {
			this.parent.removeChild(this._itemIconSprite);
			this._itemIconSprite = null;
		}
	};
	
	//-----------------------------------------------------------------------------
	// Game_Temp

	const _Game_Temp_initialize = Game_Temp.prototype.initialize;
	Game_Temp.prototype.initialize = function() {
		_Game_Temp_initialize.call(this);
		this._itemIconQueue = [];
	};

	Game_Temp.prototype.requestItemIcon = function(target, itemIconId = 0) {
		const request = { target: target, itemIconId: itemIconId };
		target.setItemIconId(itemIconId);
		this._itemIconQueue.push(request);
	};

	Game_Temp.prototype.retrieveItemIcon = function() {
		return this._itemIconQueue.shift();
	};

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

	const _Game_CharacterBase_initMembers = Game_CharacterBase.prototype.initMembers;
	Game_CharacterBase.prototype.initMembers = function() {
		_Game_CharacterBase_initMembers.call(this);
		this._itemIconId = 0;
	};

	Game_CharacterBase.prototype.setIcon = function (itemIconId, type){
		$gameTemp.requestItemIcon(this, convertIconIndex(itemIconId, type));
	};

	function convertIconIndex(index, type) {
		if (index && type) {
			let data = null;
			switch (type) {
			case "item":
			case "アイテム":
				data = $dataItems;
				break;
			case "weapon":
			case "武器":
				data = $dataWeapons;
				break;
			case "armor":
			case "防具":
				data = $dataArmors;
				break;
			case "skill":
			case "スキル":
				data = $dataSkills;
				break;
			case "state":
			case "ステート":
				data = $dataStates;
				break;
			}
			if (data && data[index]) {
				return data[index].iconIndex;
			}
			return 0;
		}
		return index || 0;
	}

	Game_CharacterBase.prototype.setItemIconId = function(itemIconId) {
		this._itemIconId = itemIconId;
	};

	Game_CharacterBase.prototype.itemIconId = function() {
		return this._itemIconId;
	};

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

	const _Game_Event_clearPageSettings = Game_Event.prototype.clearPageSettings;
	Game_Event.prototype.clearPageSettings = function() {
		_Game_Event_clearPageSettings.call(this);
		this.setIcon();
	};
	
	const _Game_Event_setupPageSettings = Game_Event.prototype.setupPageSettings;
	Game_Event.prototype.setupPageSettings = function() {
		_Game_Event_setupPageSettings.call(this);
		this.setupSettingsItemIcon();
	};
	
	Game_Event.prototype.setupSettingsItemIcon = function() {
		const page = this.page();
		this.setIcon(page.itemIcon);
	};

	//-----------------------------------------------------------------------------
	// DataManager
	//マップデータロード時の処理に追加

	const _DataManager_onLoad = DataManager.onLoad;
	DataManager.onLoad = function(object) {
		_DataManager_onLoad.call(this, object);
		if (!!(object.data && object.events) && Array.isArray(object.events)) {
			for (const event of object.events) {
				if (event && event.pages){
					extractMetadata(event);
				}
			}
		}
	};

	function extractMetadata(data) {
		for (const page of data.pages) {
			const comment = findComment(page);
			const commentData = {"note": comment};
			DataManager.extractMetadata(commentData);
			addPageSettings(page, commentData.meta);
		}
	}

	function findComment(page) {
		const list = page.list;
		if (!list[0] && list[0].code !== 108) {
			return "";
		}
		let comment = list[0].parameters[0];
		for (let i = 1; list[i] && list[i].code === 408; i++) {
			comment += list[i].parameters[0];
		}
		return comment;
	}

	function addPageSettings(page, meta) {
		const data = (meta['itemIcon'] || "").split(",");
		page.itemIcon = convertIconIndex(Number(data[0] || 0), data[1]);
	}

}