diff --git a/.gitignore b/.gitignore
index 428d58db..29d7c761 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,4 @@ game/**/*
todo.md
corescript/**/*
corescript.zip
+package-lock.json
diff --git a/js/rpg_core/Graphics.js b/js/rpg_core/Graphics.js
index 93a252a0..cd5b5429 100644
--- a/js/rpg_core/Graphics.js
+++ b/js/rpg_core/Graphics.js
@@ -360,6 +360,11 @@ Graphics.printLoadingError = function(url) {
if (this._errorPrinter && !this._errorShowed) {
this._updateErrorPrinter();
this._errorPrinter.innerHTML = this._makeErrorHtml('Loading Error', 'Failed to load: ' + url);
+ this._errorPrinter.style.userSelect = 'text';
+ this._errorPrinter.style.webkitUserSelect = 'text';
+ this._errorPrinter.style.msUserSelect = 'text';
+ this._errorPrinter.style.mozUserSelect = 'text';
+ this._errorPrinter.oncontextmenu = null; // enable context menu
var button = document.createElement('button');
button.innerHTML = 'Retry';
button.style.fontSize = '24px';
@@ -385,6 +390,11 @@ Graphics.printLoadingError = function(url) {
Graphics.eraseLoadingError = function() {
if (this._errorPrinter && !this._errorShowed) {
this._errorPrinter.innerHTML = '';
+ this._errorPrinter.style.userSelect = 'none';
+ this._errorPrinter.style.webkitUserSelect = 'none';
+ this._errorPrinter.style.msUserSelect = 'none';
+ this._errorPrinter.style.mozUserSelect = 'none';
+ this._errorPrinter.oncontextmenu = function() { return false; };
this.startLoading();
}
};
@@ -405,27 +415,32 @@ Graphics.printError = function(name, message) {
if (this._errorPrinter) {
this._updateErrorPrinter();
this._errorPrinter.innerHTML = this._makeErrorHtml(name, message);
- this._makeErrorMessage();
+ this._errorPrinter.style.userSelect = 'text';
+ this._errorPrinter.style.webkitUserSelect = 'text';
+ this._errorPrinter.style.msUserSelect = 'text';
+ this._errorPrinter.style.mozUserSelect = 'text';
+ this._errorPrinter.oncontextmenu = null; // enable context menu
+ if (this._errorMessage) {
+ this._makeErrorMessage();
+ }
}
this._applyCanvasFilter();
this._clearUpperCanvas();
};
/**
- * Shows the stacktrace of error.
+ * Shows the detail of error.
*
* @static
- * @method printStackTrace
+ * @method printErrorDetail
*/
-Graphics.printStackTrace = function(stack) {
- if (this._errorPrinter) {
- stack = (stack || '')
- .replace(/file:.*js\//g, '')
- .replace(/http:.*js\//g, '')
- .replace(/https:.*js\//g, '')
- .replace(/chrome-extension:.*js\//g, '')
- .replace(/\n/g, '
');
- this._makeStackTrace(decodeURIComponent(stack));
+Graphics.printErrorDetail = function(error) {
+ if (this._errorPrinter && this._showErrorDetail) {
+ var eventInfo = this._formatEventInfo(error);
+ var eventCommandInfo = this._formatEventCommandInfo(error);
+ var info = eventCommandInfo ? eventInfo + ", " + eventCommandInfo : eventInfo;
+ var stack = this._formatStackTrace(error);
+ this._makeErrorDetail(info, stack);
}
};
@@ -439,6 +454,16 @@ Graphics.setErrorMessage = function(message) {
this._errorMessage = message;
};
+/**
+ * Sets whether shows the detail of error.
+ *
+ * @static
+ * @method setShowErrorDetail
+ */
+Graphics.setShowErrorDetail = function(showErrorDetail) {
+ this._showErrorDetail = showErrorDetail;
+};
+
/**
* Shows the FPSMeter element.
*
@@ -862,16 +887,17 @@ Graphics._createErrorPrinter = function() {
*/
Graphics._updateErrorPrinter = function() {
this._errorPrinter.width = this._width * 0.9;
- this._errorPrinter.height = this._errorShowed ? this._height * 0.9 : 40;
+ if (this._errorShowed && this._showErrorDetail) {
+ this._errorPrinter.height = this._height * 0.9;
+ } else if (this._errorShowed && this._errorMessage) {
+ this._errorPrinter.height = 100;
+ } else {
+ this._errorPrinter.height = 40;
+ }
this._errorPrinter.style.textAlign = 'center';
this._errorPrinter.style.textShadow = '1px 1px 3px #000';
this._errorPrinter.style.fontSize = '20px';
this._errorPrinter.style.zIndex = 99;
- this._errorPrinter.style.userSelect = 'text';
- this._errorPrinter.style.webkitUserSelect = 'text';
- this._errorPrinter.style.msUserSelect = 'text';
- this._errorPrinter.style.mozUserSelect = 'text';
- this._errorPrinter.oncontextmenu = null; // enable context menu
this._centerElement(this._errorPrinter);
};
@@ -886,23 +912,82 @@ Graphics._makeErrorMessage = function() {
style.color = 'white';
style.textAlign = 'left';
style.fontSize = '18px';
- mainMessage.innerHTML = '
' + (this._errorMessage || '');
+ mainMessage.innerHTML = '
' + this._errorMessage;
this._errorPrinter.appendChild(mainMessage);
};
/**
* @static
- * @method _makeStackTrace
+ * @method _makeErrorDetail
* @private
*/
-Graphics._makeStackTrace = function(stack) {
- var stackTrace = document.createElement('div');
- var style = stackTrace.style;
+Graphics._makeErrorDetail = function(info, stack) {
+ var detail = document.createElement('div');
+ var style = detail.style;
style.color = 'white';
style.textAlign = 'left';
style.fontSize = '18px';
- stackTrace.innerHTML = '
' + stack + '
';
- this._errorPrinter.appendChild(stackTrace);
+ detail.innerHTML = '
' + info + '
' + stack;
+ this._errorPrinter.appendChild(detail);
+};
+
+/**
+ * @static
+ * @method _formatEventInfo
+ * @private
+ */
+Graphics._formatEventInfo = function(error) {
+ switch (String(error.eventType)) {
+ case "map_event":
+ return "MapID: %1, MapEventID: %2, page: %3, line: %4".format(error.mapId, error.mapEventId, error.page, error.line);
+ case "common_event":
+ return "CommonEventID: %1, line: %2".format(error.commonEventId, error.line);
+ case "battle_event":
+ return "TroopID: %1, page: %2, line: %3".format(error.troopId, error.page, error.line);
+ case "test_event":
+ return "TestEvent, line: %1".format(error.line);
+ default:
+ return "No information";
+ }
+};
+
+/**
+ * @static
+ * @method _formatEventCommandInfo
+ * @private
+ */
+Graphics._formatEventCommandInfo = function(error) {
+ switch (String(error.eventCommand)) {
+ case "plugin_command":
+ return "◆Plugin Command: " + error.content;
+ case "script":
+ return "◆Script: " + error.content;
+ case "control_variables":
+ return "◆Control Variables: Script: " + error.content;
+ case "conditional_branch_script":
+ return "◆If: Script: " + error.content;
+ case "set_route_script":
+ return "◆Set Movement Route: ◇Script: " + error.content;
+ case "auto_route_script":
+ return "Autonomous Movement Custom Route: ◇Script: " + error.content;
+ case "other":
+ default:
+ return "";
+ }
+};
+
+/**
+ * @static
+ * @method _formatStackTrace
+ * @private
+ */
+Graphics._formatStackTrace = function(error) {
+ return decodeURIComponent((error.stack || '')
+ .replace(/file:.*js\//g, '')
+ .replace(/http:.*js\//g, '')
+ .replace(/https:.*js\//g, '')
+ .replace(/chrome-extension:.*js\//g, '')
+ .replace(/\n/g, '
'));
};
/**
diff --git a/js/rpg_managers/SceneManager.js b/js/rpg_managers/SceneManager.js
index d4c8d804..0463187e 100644
--- a/js/rpg_managers/SceneManager.js
+++ b/js/rpg_managers/SceneManager.js
@@ -200,7 +200,7 @@ SceneManager.onKeyDown = function(event) {
SceneManager.catchException = function(e) {
if (e instanceof Error) {
Graphics.printError(e.name, e.message);
- Graphics.printStackTrace(e.stack);
+ Graphics.printErrorDetail(e);
console.error(e.stack);
} else {
Graphics.printError('UnknownError', e);
diff --git a/js/rpg_objects/Game_Character.js b/js/rpg_objects/Game_Character.js
index 85bfed0d..ae731ead 100644
--- a/js/rpg_objects/Game_Character.js
+++ b/js/rpg_objects/Game_Character.js
@@ -69,6 +69,7 @@ Game_Character.prototype.initMembers = function() {
this._originalMoveRoute = null;
this._originalMoveRouteIndex = 0;
this._waitCount = 0;
+ this._callerEventInfo = null;
};
Game_Character.prototype.memorizeMoveRoute = function() {
@@ -80,6 +81,7 @@ Game_Character.prototype.restoreMoveRoute = function() {
this._moveRoute = this._originalMoveRoute;
this._moveRouteIndex = this._originalMoveRouteIndex;
this._originalMoveRoute = null;
+ this._callerEventInfo = null;
};
Game_Character.prototype.isMoveRouteForcing = function() {
@@ -102,6 +104,10 @@ Game_Character.prototype.forceMoveRoute = function(moveRoute) {
this._waitCount = 0;
};
+Game_Character.prototype.setCallerEventInfo = function(callerEventInfo) {
+ this._callerEventInfo = callerEventInfo;
+};
+
Game_Character.prototype.updateStop = function() {
Game_CharacterBase.prototype.updateStop.call(this);
if (this._moveRouteForcing) {
@@ -262,7 +268,27 @@ Game_Character.prototype.processMoveCommand = function(command) {
AudioManager.playSe(params[0]);
break;
case gc.ROUTE_SCRIPT:
- eval(params[0]);
+ try {
+ eval(params[0]);
+ } catch (error) {
+ if (this._callerEventInfo) {
+ for (var key in this._callerEventInfo) {
+ error[key] = this._callerEventInfo[key];
+ }
+ error.line += this._moveRouteIndex + 1;
+ error.eventCommand = "set_route_script";
+ error.content = command.parameters[0];
+ } else {
+ error.eventType = "map_event";
+ error.mapId = this._mapId;
+ error.mapEventId = this._eventId;
+ error.page = this._pageIndex + 1;
+ error.line = this._moveRouteIndex + 1;
+ error.eventCommand = "auto_route_script";
+ error.content = command.parameters[0];
+ }
+ throw error;
+ }
break;
}
};
diff --git a/js/rpg_objects/Game_CommonEvent.js b/js/rpg_objects/Game_CommonEvent.js
index 5a9a707c..611152a7 100644
--- a/js/rpg_objects/Game_CommonEvent.js
+++ b/js/rpg_objects/Game_CommonEvent.js
@@ -40,6 +40,7 @@ Game_CommonEvent.prototype.update = function() {
if (this._interpreter) {
if (!this._interpreter.isRunning()) {
this._interpreter.setup(this.list());
+ this._interpreter.setEventInfo({ eventType: 'common_event', commonEventId: this._commonEventId });
}
this._interpreter.update();
}
diff --git a/js/rpg_objects/Game_Event.js b/js/rpg_objects/Game_Event.js
index e1924ba7..cf570c2c 100644
--- a/js/rpg_objects/Game_Event.js
+++ b/js/rpg_objects/Game_Event.js
@@ -322,6 +322,7 @@ Game_Event.prototype.updateParallel = function() {
if (this._interpreter) {
if (!this._interpreter.isRunning()) {
this._interpreter.setup(this.list(), this._eventId);
+ this._interpreter.setEventInfo(this.getEventInfo());
}
this._interpreter.update();
}
@@ -336,3 +337,7 @@ Game_Event.prototype.forceMoveRoute = function(moveRoute) {
Game_Character.prototype.forceMoveRoute.call(this, moveRoute);
this._prelockDirection = 0;
};
+
+Game_Event.prototype.getEventInfo = function() {
+ return { eventType: "map_event", mapId: this._mapId, mapEventId: this._eventId, page: this._pageIndex + 1 };
+};
diff --git a/js/rpg_objects/Game_Interpreter.js b/js/rpg_objects/Game_Interpreter.js
index 56b36b83..33d709bc 100644
--- a/js/rpg_objects/Game_Interpreter.js
+++ b/js/rpg_objects/Game_Interpreter.js
@@ -32,6 +32,7 @@ Game_Interpreter.prototype.clear = function() {
this._waitCount = 0;
this._waitMode = '';
this._comments = '';
+ this._eventInfo = null;
this._character = null;
this._childInterpreter = null;
};
@@ -52,9 +53,14 @@ Game_Interpreter.prototype.isOnCurrentMap = function() {
return this._mapId === $gameMap.mapId();
};
+Game_Interpreter.prototype.setEventInfo = function(eventInfo) {
+ this._eventInfo = eventInfo;
+};
+
Game_Interpreter.prototype.setupReservedCommonEvent = function() {
if ($gameTemp.isCommonEventReserved()) {
this.setup($gameTemp.reservedCommonEvent().list);
+ this.setEventInfo({ eventType: 'common_event', commonEventId: $gameTemp.reservedCommonEventId() });
$gameTemp.clearCommonEvent();
return true;
} else {
@@ -166,8 +172,17 @@ Game_Interpreter.prototype.executeCommand = function() {
this._indent = command.indent;
var methodName = 'command' + command.code;
if (typeof this[methodName] === 'function') {
- if (!this[methodName]()) {
- return false;
+ try {
+ if (!this[methodName]()) {
+ return false;
+ }
+ } catch (error) {
+ for (var key in this._eventInfo) {
+ error[key] = this._eventInfo[key];
+ }
+ error.eventCommand = error.eventCommand || "other";
+ error.line = error.line || this._index + 1;
+ throw error;
}
}
this._index++;
@@ -543,7 +558,13 @@ Game_Interpreter.prototype.command111 = function() {
result = Input.isPressed(this._params[1]);
break;
case 12: // Script
- result = !!eval(this._params[1]);
+ try {
+ result = !!eval(this._params[1]);
+ } catch (error) {
+ error.eventCommand = "conditional_branch_script";
+ error.content = this._params[1];
+ throw error;
+ }
break;
case 13: // Vehicle
result = ($gamePlayer.vehicle() === $gameMap.vehicle(this._params[1]));
@@ -616,6 +637,7 @@ Game_Interpreter.prototype.command117 = function() {
Game_Interpreter.prototype.setupChild = function(list, eventId) {
this._childInterpreter = new Game_Interpreter(this._depth + 1);
this._childInterpreter.setup(list, eventId);
+ this._childInterpreter.setEventInfo({ eventType: 'common_event', commonEventId: this._params[0] });
};
// Label
@@ -680,7 +702,13 @@ Game_Interpreter.prototype.command122 = function() {
value = this.gameDataOperand(this._params[4], this._params[5], this._params[6]);
break;
case 4: // Script
- value = eval(this._params[4]);
+ try {
+ value = eval(this._params[4]);
+ } catch (error) {
+ error.eventCommand = "control_variables";
+ error.content = this._params[4];
+ throw error;
+ }
break;
}
for (var i = this._params[0]; i <= this._params[1]; i++) {
@@ -1024,6 +1052,9 @@ Game_Interpreter.prototype.command205 = function() {
this._character = this.character(this._params[0]);
if (this._character) {
this._character.forceMoveRoute(this._params[1]);
+ var eventInfo = JsonEx.makeDeepCopy(this._eventInfo);
+ eventInfo.line = this._index + 1;
+ this._character.setCallerEventInfo(eventInfo);
if (this._params[1].wait) {
this.setWaitMode('route');
}
@@ -1731,12 +1762,21 @@ Game_Interpreter.prototype.command354 = function() {
// Script
Game_Interpreter.prototype.command355 = function() {
+ var startLine = this._index + 1;
var script = this.currentCommand().parameters[0] + '\n';
while (this.nextEventCode() === 655) {
this._index++;
script += this.currentCommand().parameters[0] + '\n';
}
- eval(script);
+ var endLine = this._index + 1;
+ try {
+ eval(script);
+ } catch (error) {
+ error.line = startLine + "-" + endLine;
+ error.eventCommand = "script";
+ error.content = script;
+ throw error;
+ }
return true;
};
@@ -1744,7 +1784,13 @@ Game_Interpreter.prototype.command355 = function() {
Game_Interpreter.prototype.command356 = function() {
var args = this._params[0].split(" ");
var command = args.shift();
- this.pluginCommand(command, args);
+ try {
+ this.pluginCommand(command, args);
+ } catch (error) {
+ error.eventCommand = "plugin_command";
+ error.content = this._params[0];
+ throw error;
+ }
return true;
};
diff --git a/js/rpg_objects/Game_Map.js b/js/rpg_objects/Game_Map.js
index 10352822..fb6ed9c6 100644
--- a/js/rpg_objects/Game_Map.js
+++ b/js/rpg_objects/Game_Map.js
@@ -756,6 +756,7 @@ Game_Map.prototype.setupStartingEvent = function() {
Game_Map.prototype.setupTestEvent = function() {
if ($testEvent) {
this._interpreter.setup($testEvent, 0);
+ this._interpreter.setEventInfo({ eventType: 'test_event' });
$testEvent = null;
return true;
}
@@ -769,6 +770,7 @@ Game_Map.prototype.setupStartingMapEvent = function() {
if (event.isStarting()) {
event.clearStartingFlag();
this._interpreter.setup(event.list(), event.eventId());
+ this._interpreter.setEventInfo(event.getEventInfo());
return true;
}
}
@@ -780,6 +782,7 @@ Game_Map.prototype.setupAutorunCommonEvent = function() {
var event = $dataCommonEvents[i];
if (event && event.trigger === 1 && $gameSwitches.value(event.switchId)) {
this._interpreter.setup(event.list);
+ this._interpreter.setEventInfo({ eventType: 'common_event', commonEventId: i });
return true;
}
}
diff --git a/js/rpg_objects/Game_Temp.js b/js/rpg_objects/Game_Temp.js
index a3c59be1..db3b1fc0 100644
--- a/js/rpg_objects/Game_Temp.js
+++ b/js/rpg_objects/Game_Temp.js
@@ -34,6 +34,10 @@ Game_Temp.prototype.reservedCommonEvent = function() {
return $dataCommonEvents[this._commonEventId];
};
+Game_Temp.prototype.reservedCommonEventId = function() {
+ return this._commonEventId;
+};
+
Game_Temp.prototype.setDestination = function(x, y) {
this._destinationX = x;
this._destinationY = y;
diff --git a/js/rpg_objects/Game_Troop.js b/js/rpg_objects/Game_Troop.js
index fb520c5c..4be2c76c 100644
--- a/js/rpg_objects/Game_Troop.js
+++ b/js/rpg_objects/Game_Troop.js
@@ -159,6 +159,7 @@ Game_Troop.prototype.setupBattleEvent = function() {
var page = pages[i];
if (this.meetsConditions(page) && !this._eventFlags[i]) {
this._interpreter.setup(page.list);
+ this._interpreter.setEventInfo({ eventType: 'battle_event', troopId: this._troopId, page: i + 1 });
if (page.span <= 1) {
this._eventFlags[i] = true;
}
diff --git a/package.json b/package.json
index b07040f6..1d1a1fb9 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,11 @@
"build:windows": "node ./concat.js rpg_windows",
"watch": "node ./watch.js",
"build": "run-p build:*",
- "copy:corescripts": "cpx ./dist/*.js ./game/js/",
- "copy:libs": "cpx ./js/libs/*.js ./game/js/libs/",
- "copy:main": "cpx ./js/main.js ./game/js/",
- "copy:plugins": "cpx ./plugins/*.js ./game/js/plugins/",
- "copy:template": "cpx ./template/**/* ./game/",
+ "copy:corescripts": "cpx './dist/*.js' ./game/js/",
+ "copy:libs": "cpx './js/libs/*.js' ./game/js/libs/",
+ "copy:main": "cpx './js/main.js' ./game/js/",
+ "copy:plugins": "cpx './plugins/*.js' ./game/js/plugins/",
+ "copy:template": "cpx './template/**/*' ./game/",
"copy": "run-p copy:*",
"copy-all": "node copy-all.js ./corescript",
"zip": "bestzip corescript.zip ./corescript/",
diff --git a/plugins/Community_Basic.js b/plugins/Community_Basic.js
index 0a05828f..be9ed186 100644
--- a/plugins/Community_Basic.js
+++ b/plugins/Community_Basic.js
@@ -64,6 +64,11 @@
* @desc The message when error occurred
* @default Error occurred. Please ask to the creator of this game.
*
+ * @param showErrorDetail
+ * @type boolean
+ * @desc Show where the error is caused and stack trace when error
+ * @default true
+ *
* @param enableProgressBar
* @type boolean
* @desc Show progress bar when it takes a long time to load resources
@@ -144,8 +149,15 @@
* @param errorMessage
* @type string
* @text エラーメッセージ
+ * @desc エラー時にプレイヤーに向けて表示するメッセージです
* @default エラーが発生しました。ゲームの作者にご連絡ください。
*
+ * @param showErrorDetail
+ * @type boolean
+ * @text エラー詳細表示
+ * @desc ONにすると、エラー時にエラーを発生させたイベントの情報とスタックトレースを表示します
+ * @default true
+ *
* @param enableProgressBar
* @type boolean
* @text ロード進捗バー有効化
@@ -181,6 +193,7 @@
var maxRenderingFps = toNumber(parameters['maxRenderingFps'], 0);
var autoSaveFileId = toNumber(parameters['autoSaveFileId'], 0);
var errorMessage = parameters['errorMessage'];
+ var showErrorDetail = parameters['showErrorDetail'] === 'true';
var enableProgressBar = parameters['enableProgressBar'] === 'true';
var windowWidth;
@@ -263,5 +276,6 @@
DataManager.setAutoSaveFileId(autoSaveFileId);
Graphics.setErrorMessage(errorMessage);
+ Graphics.setShowErrorDetail(showErrorDetail);
Graphics.setProgressEnabled(enableProgressBar);
})();
\ No newline at end of file