//=============================================================================
// NpcFollower.js
// ----------------------------------------------------------------------------
// (C) 2016 Triacontane
// This software is released under the MIT License.
// [url]http://opensource.org/licenses/mit-license.php[/url]
// ----------------------------------------------------------------------------
// Version
// 1.1.2 2022/05/10 パラメータの説明文を修正
// 1.1.1 2019/11/02 1.1.0の修正でメンバーの入れ替えを実施すると表示が不正になる場合がある問題を修正
// 1.1.0 2019/01/27 通常のフォロワーを表示せず、NPCフォロワーのみを表示できる機能を追加
// 1.0.3 2018/08/06 コアスクリプトが1.6.0より古い場合にエラーになる記述を修正
// 1.0.2 2017/01/17 プラグインコマンドが小文字でも動作するよう修正(byこまちゃん先輩)
// 1.0.1 2016/07/17 セーブデータをロードした際のエラーになる現象の修正
// 1.0.0 2016/07/14 初版
// ----------------------------------------------------------------------------
// [Blog] : [url]https://triacontane.blogspot.jp/[/url]
// [Twitter]: [url]https://twitter.com/triacontane/[/url]
// [GitHub] : [url]https://github.com/triacontane/[/url]
//=============================================================================
/*:
* @plugindesc NPC 跟随插件
* @author triacontane
*
* @param MaxNpcNumber
* @desc 队列中可以同时存在的 npc 的最大数量。
* @default 1
*
* @param HideNormalFollower
* @desc 不显示常规跟随者,仅显示 npc 跟随者。启用数据库列队行走。
* @default false
* @type boolean
*
* @help 将队员以外的NPC添加到地图上排中的任何位置。
* NPC 在数据库中由 Actor 定义,然后从插件命令中添加或删除。
* 因为不是战斗员,所以不会影响菜单画面和战斗画面。
* 此外,如果不显示编队,则不会显示任何内容。
*
* 您还可以添加具有相同 ID 的多个角色。
*
* 插件命令详细信息
* 从事件命令“插件命令”运行。
* (参数之间用半角空格分隔)
*
* NF_NPC追加 4 1 # 增加ID为[4]的角色为NPC,加入队伍第1个人后面
* NF_ADD_NPC 5 3 # 将角色 ID [5]作为 npc 添加到队伍第3个人后面
* NF_NPC削除 1 # [1]番目に追加したNPCを全て削除
* NF_REM_NPC 3 # [3]番目に追加したNPCを全て削除
*
* 索引指定与添加的NPC无关,而是与队伍的顺序有关(1... 战斗成员的数量)。。
*
* This plugin is released under the MIT License.
*/
/*:ja
* @plugindesc NPCフォロワープラグイン
* @author トリアコンタン
*
* @param 最大同時NPC数
* @desc 同時に隊列に存在できるNPCの最大数です。
* @default 1
* @type number
* @min 1
*
* @param 通常フォロワーを表示しない
* @desc 通常のフォロワーを一切表示せず、NPCフォロワーのみを表示します。データベースの隊列歩行は有効にしてください。
* @default false
* @type boolean
*
* @help マップ上の隊列の好きな位置にパーティメンバー以外のNPCを追加します。
* NPCはデータベース上はアクターで定義してプラグインコマンドから追加、削除します。
* 戦闘員ではないので、メニュー画面や戦闘画面には影響を与えません。
* また、隊列表示していない場合は何も表示されません。
*
* 同一IDのアクターを複数追加することもできます。
*
* プラグインコマンド詳細
* イベントコマンド「プラグインコマンド」から実行。
* (パラメータの間は半角スペースで区切る)
*
* NF_NPC追加 4 1 # アクターID[4]をNPCとしてパーティの[1]番目の後ろに追加
* NF_ADD_NPC 5 3 # アクターID[5]をNPCとしてパーティの[3]番目の後ろに追加
* NF_NPC削除 1 # [1]番目に追加したNPCを全て削除
* NF_REM_NPC 3 # [3]番目に追加したNPCを全て削除
*
* インデックスの指定は追加したNPCとは関係なく、
* パーティの並び順(1...バトルメンバー数)を指定してください。
*
* 利用規約:
* 作者に無断で改変、再配布が可能で、利用形態(商用、18禁利用等)
* についても制限はありません。
* このプラグインはもうあなたのものです。
*/
(function() {
'use strict';
var pluginName = 'NpcFollower';
var metaTagPrefix = 'NF';
var getCommandName = function(command) {
return (command || '').toUpperCase();
};
var getParamOther = function(paramNames) {
if (!Array.isArray(paramNames)) paramNames = [paramNames];
for (var i = 0; i < paramNames.length; i++) {
var name = PluginManager.parameters(pluginName)[paramNames[i]];
if (name) return name;
}
return null;
};
var getParamBoolean = function(paramNames) {
var value = getParamOther(paramNames);
return (value || '').toUpperCase() === 'TRUE';
};
var getParamNumber = function(paramNames, min, max) {
var value = getParamOther(paramNames);
if (arguments.length < 2) min = -Infinity;
if (arguments.length < 3) max = Infinity;
return (parseInt(value, 10) || 0).clamp(min, max);
};
var getArgNumberWithEval = function(arg, min, max) {
if (arguments.length < 2) min = -Infinity;
if (arguments.length < 3) max = Infinity;
return (parseInt(eval(convertEscapeCharacters(arg)), 10) || 0).clamp(min, max);
};
var convertEscapeCharacters = function(text) {
if (text == null) text = '';
var windowLayer = SceneManager._scene._windowLayer;
return windowLayer ? windowLayer.children[0].convertEscapeCharacters(text) : text;
};
//=============================================================================
// パラメータの取得と整形
//=============================================================================
var paramMaxNpcNumber = getParamNumber(['MaxNpcNumber', '最大同時NPC数']);
var paramHideNormalFollower = getParamBoolean(['HideNormalFollower', '通常フォロワーを表示しない']);
//=============================================================================
// Game_Interpreter
// プラグインコマンドを追加定義します。
//=============================================================================
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
_Game_Interpreter_pluginCommand.apply(this, arguments);
this.pluginCommandNpcFollower(command.replace(new RegExp(metaTagPrefix, 'i'), ''), args);
};
Game_Interpreter.prototype.pluginCommandNpcFollower = function(command, args) {
switch (getCommandName(command)) {
case '_NPC追加' :
case '_ADD_NPC' :
var actorId = getArgNumberWithEval(args[0]);
$gameParty.addNpc(actorId, getArgNumberWithEval(args[1], 1));
break;
case '_NPC削除' :
case '_REM_NPC' :
$gameParty.removeNpc(getArgNumberWithEval(args[0], 1));
break;
}
};
//=============================================================================
// Game_Party
// NPCの追加と削除を追加定義します。
//=============================================================================
var _Game_Party_initialize = Game_Party.prototype.initialize;
Game_Party.prototype.initialize = function() {
_Game_Party_initialize.apply(this, arguments);
this.initNpc();
};
Game_Party.prototype.initNpc = function() {
this._npcs = [];
this._npcIndexes = [];
};
Game_Party.prototype.isNpcInvalid = function() {
return !this._npcs;
};
Game_Party.prototype.initNpcIfNeed = function() {
if (this.isNpcInvalid()) {
this.initNpc();
$gamePlayer.followers().initNpc();
}
};
Game_Party.prototype.addNpc = function(actorId, index) {
if (this._npcs.length < paramMaxNpcNumber) {
this._npcs.push(actorId);
this._npcIndexes.push(index);
$gamePlayer.refresh();
$gameMap.requestRefresh();
} else {
throw new Error('登録可能な最大数を超えています。');
}
};
Game_Party.prototype.removeNpc = function(index) {
for (var i = 0, n = this._npcs.length; i < n; i++) {
if (this._npcIndexes[i] === index) {
this._npcs.splice(i, 1);
this._npcIndexes.splice(i, 1);
i--;
}
}
$gamePlayer.refresh();
$gameMap.requestRefresh();
};
Game_Party.prototype.npcMembers = function() {
return this._npcs.map(function(id) {
return $gameActors.actor(id);
});
};
Game_Party.prototype.visibleMembers = function() {
return this._visibleMembers;
};
Game_Party.prototype.makeVisibleMembers = function() {
var battleMembers = this.battleMembers();
var npcMembers = this.npcMembers();
var visibleMembers = [];
for (var i = 0, n = this.maxBattleMembers() + 1; i < n; i++) {
for (var j = 0, m = npcMembers.length; j < m; j++) {
if (this._npcIndexes[j] === i) {
visibleMembers.push(npcMembers[j]);
}
}
if (battleMembers.length > i && !paramHideNormalFollower) {
visibleMembers.push(battleMembers[i]);
}
}
this._visibleMembers = visibleMembers;
};
//=============================================================================
// Game_Followers
// NPCの最大数ぶんだけ余分にGame_Followerを作成します。
//=============================================================================
var _Game_Followers_initialize = Game_Followers.prototype.initialize;
Game_Followers.prototype.initialize = function() {
_Game_Followers_initialize.apply(this, arguments);
if (paramHideNormalFollower) {
this._data = [];
}
this.initNpc();
};
Game_Followers.prototype.initNpc = function() {
var memberLength = this._data.length > 0 ? this._data.length + 1 : 0;
for (var i = 0; i < paramMaxNpcNumber; i++) {
this._data.push(new Game_Follower(memberLength + i));
}
};
//=============================================================================
// Game_Follower
// NPC判定を追加定義します。
//=============================================================================
var _Game_Follower_actor = Game_Follower.prototype.actor;
Game_Follower.prototype.actor = function() {
_Game_Follower_actor.apply(this, arguments);
return $gameParty.visibleMembers()[this._memberIndex];
};
//=============================================================================
// Game_Player
// リフレッシュまえに表示メンバーを更新します。
//=============================================================================
var _Game_Player_refresh = Game_Player.prototype.refresh;
Game_Player.prototype.refresh = function() {
$gameParty.makeVisibleMembers();
_Game_Player_refresh.apply(this, arguments);
};
//=============================================================================
// DataManager
// プラグイン未適用のデータをロードした場合に必要なデータを初期化します。
//=============================================================================
var _DataManager_loadGame = DataManager.loadGame;
DataManager.loadGame = function(saveFileId) {
var result = _DataManager_loadGame.apply(this, arguments);
$gameParty.initNpcIfNeed();
return result;
};
})();