/**
* @namespace MATTIE.unstuckAPI
* @description The api that handles everything related to providing ways to get unstuck in the event of errors
* One of the biggest features is the ability to switch any currently running attorn events to be parallel instead.
*/
MATTIE.unstuckAPI = MATTIE.unstuckAPI || {};
/**
* @description whether player free move is toggled on or off. that is whether players can move regardless of msgbox and such
* Multiplayer will turn this on by default
* */
MATTIE.unstuckAPI.playerFreeMove = false;
/**
* @description whether once every time an area loads unstuck will run
* */
MATTIE.unstuckAPI.autoUnstuckOn = false;
/**
* @description convert all currently running autorun events to parallel events --there by breaking out of any blocking loop
* @param {int} repairInMs if provided after this many milliseconds, revert the changes made, repairing the map to its original state.
* @param {boolean} sequential whether to move all of these to be parallel sequentially on the same interpreter (usually a good idea)
*/
MATTIE.unstuckAPI.convertRunningAutorunToParallel = function (repairInMs = -1, sequential = true) {
const autoRunEvents = MATTIE.util.getAllAutoRunEvents();
const interpreter = new Game_Interpreter();
interpreter.setup($gameMap._interpreter._list, $gameMap._interpreter._eventId); // transfer list from main interpreter to secondary
const pages = [];
for (let index = 0; index < autoRunEvents.length; index++) {
const event = autoRunEvents[index];
event._trigger = 4; // set to parallel
event._starting = false;
event.sequential = sequential;
// set the page's trigger and create an interpreter for it since it is now parellel and needs one
if (event.page()) {
if (event.page().trigger === 3) {
event.page().trigger = 4;
pages.push(event.page());
event._interpreter = sequential ? interpreter : new Game_Interpreter(); // give us a new interpreter for the parellel event
}
}
event.refresh();
}
if (repairInMs > 0) { // if we should repair or not
setTimeout(() => {
// cleanup everything done above
for (let index = 0; index < autoRunEvents.length; index++) {
const event = autoRunEvents[index];
if (event.page()) { if (event.page().trigger === 4) event._trigger = 3; } else { event._trigger = null; } // this is likely unneeded
event._starting = false;
event._interpreter = null;
event.refresh();
}
pages.forEach((page) => {
page.trigger = 3;
});
}, repairInMs);
}
// clear our main interpreter
$gameMap._interpreter.clear();
$gameMap._interpreter._list = null;
};
/**
* @deprecated
* @description convert all currently running autorun events to non-blocking autorun events, that is make them run on a different interpreter than the
* main map interpreter
* @param {int} repairInMs if provided after this many milliseconds, revert the changes made, repairing the map to its original state.
* @param {Game_Interpreter} interpreter the interpreter to run these events on
*/
MATTIE.unstuckAPI.convertRunningAutorunToNonBlockingAutorun = function (repairInMs = -1, interpreter = new Game_Interpreter()) {
const autoRunEvents = MATTIE.util.getAllAutoRunEvents();
// override the update interpreter method to also update our secondary interpreter
const oldUpdate = Game_Map.prototype.updateInterpreter;
Game_Map.prototype.updateInterpreter = function () {
oldUpdate.call(this);
this.updateSecondary(interpreter);
};
const pages = [];
for (let index = 0; index < autoRunEvents.length; index++) {
const event = autoRunEvents[index];
event._starting = false; // restart event
event.secondaryInterpreter = interpreter; // setup our other interpreter
event.refresh();
}
if (repairInMs > 0) { // if we should repair or not
setTimeout(() => {
// cleanup everything done above
for (let index = 0; index < autoRunEvents.length; index++) {
const event = autoRunEvents[index];
event._starting = true;
event.secondaryInterpreter = null;
event.refresh();
}
// cleanup our override
Game_Map.prototype.updateInterpreter = function () {
oldUpdate.call(this);
};
}, repairInMs);
}
$gameMap._interpreter.clear(); // clear the main map interpreter to exit any current blocking commands
$gameMap._interpreter._list = null;
};
/**
* @description A method that will try everything I can think of to get you unstuck, this might break some stuff, but it should get you unstuck
* @param {num} t the number of milli seconds before reverting parellel events back to autorun
*/
MATTIE.unstuckAPI.unstuck = function (t = 20000) {
this.convertRunningAutorunToParallel(t);
// make sure player is visible, can move and can open menu and save
MATTIE.fxAPI.showPlayer();
MATTIE.fxAPI.unlockPlayer();
// clear images on screen
$gameScreen.clearPictures();
};
/**
* @description toggle on or off permanent unstuck, not the best solution either, but decent enough
* @param {bool} bool; whether to turn it on or off
*/
MATTIE.unstuckAPI.togglePermanentUnstuck = function (bool) {
MATTIE.unstuckAPI.autoUnstuckOn = bool;
if (bool) {
this.unstuck(-1);
if (!this.previousLoadMethod) {
this.previousLoadMethod = Scene_Map.prototype.onMapLoaded;
}
// define that to perserve scope into scene map func
const that = this;
Scene_Map.prototype.onMapLoaded = function () {
that.previousLoadMethod.call(this);
that.unstuck(-1);
};
} else if (this.previousLoadMethod) {
Scene_Map.prototype.onMapLoaded = this.previousLoadMethod;
this.previousLoadMethod = undefined;
}
};
/**
* @description allow all entities derived from players to move freely even durring cut scenes and msgs
* this does not apply to choice msgs
* @param {bool} bool on or off
*/
MATTIE.unstuckAPI.togglePlayerFreeMove = function (bool) {
this.playerFreeMove = bool;
const that = this;
if (bool) {
if (!this.prevPlayerCanMove) {
this.prevPlayerCanMove = Game_Player.prototype.canMove;
Game_Player.prototype.canMove = function () {
return !$gameMessage.isChoice() || that.prevPlayerCanMove.call(this);
};
}
} else if (this.prevPlayerCanMove) {
Game_Player.prototype.canMove = this.prevPlayerCanMove;
}
};
/**
* @description an alias of MATTIE.unstuckAPI.unstuck meant for being called from console
* @alias MATTIE.unstuckAPI.unstuck
*/
function unstuck() {
MATTIE.unstuckAPI.unstuck();
}
//-----------------------------------------------
// Engine Overrides
//-----------------------------------------------
/**
* @deprecated
* @description override the setupstartingmap event to allow us to pass the interpreter we would like the event to run on through the event
* @returns {boolean}
*/
Game_Map.prototype.setupStartingMapEventSecondaries = function () {
var events = this.events();
for (var i = 0; i < events.length; i++) {
var event = events[i];
if (event.isStarting()) {
event.clearStartingFlag();
const interpreter = event.secondaryInterpreter;
if (interpreter) { interpreter.setup(event.list(), event.eventId()); }
return true;
}
}
return false;
};
/** @deprecated */
Game_Map.prototype.updateSecondary = function (interpreter) {
for (;;) {
interpreter.update();
if (interpreter.isRunning()) {
return;
}
if (interpreter.eventId() > 0) {
this.unlockEvent(interpreter.eventId());
interpreter.clear();
}
if (!this.setupStartingMapEventSecondaries()) {
return;
}
}
};
/** @description the base game event start method */
MATTIE_RPG.Game_Event_start = Game_Event.prototype.start;
/**
* @description override the start method to record when it started
*/
Game_Event.prototype.start = function () {
MATTIE_RPG.Game_Event_start.call(this);
this.lastRan = new Date().getTime();
};
/**
* @description returns whether this event has ran within the last x milli seconds
* @param {int} x how many miliseconds to check
* @returns {boolean} whether the above statement is true or not
*
*/
Game_Event.prototype.ranWithinSec = function (x) {
if (this.lastRan) {
return this.lastRan + x >= new Date().getTime();
}
return false;
};
/** @description the base updateParallel method */
MATTIE_RPG.Game_Event_updateParallel = Game_Event.prototype.updateParallel;
/**
* @description override the update parallel method to refresh the event if it is a sequential parallel event.
* We need to do this as parallel events trigger every frame but refresh does not trigger fast enough to keep up with those changes when we are dealing with
* sequential parallel events
* @override
*/
Game_Event.prototype.updateParallel = function () {
if (this.sequential) this.refresh();
MATTIE_RPG.Game_Event_updateParallel.call(this);
};
// hook onto on map loaded to update the setting of player free move
(() => {
const onMapLoaded = Scene_Map.prototype.onMapLoaded;
Scene_Map.prototype.onMapLoaded = function () {
onMapLoaded.call(this);
if (MATTIE.multiplayer) {
if (MATTIE.multiplayer.config) { MATTIE.unstuckAPI.togglePlayerFreeMove(MATTIE.multiplayer.config.freeMove); }
}
};
})();
// hook onto event start to check if it is auto run and make the player release their movement keys if it is
// (() => {
// let lastHitTouchEvent = 0;
// let lastMsg = 0;
// const start = Game_Event.prototype.start;
// Game_Event.prototype.start = function () {
// start.call(this);
// // if the player has recently triggered a touch event and an auto run event runs
// if (this._trigger === 3 && (Date.now() - lastHitTouchEvent < 200)) {
// // is auto run
// // release all keys
// Input._currentState = [];
// } else if (this._trigger === 1) {
// // player touch
// lastHitTouchEvent = Date.now();
// }
// };
// // override the message system to stop the player if its recent
// const add = Game_Message.prototype.add;
// Game_Message.prototype.add = function (text) {
// add.call(this, text);
// if (Date.now() - lastMsg > 5000) {
// // release all keys
// Input._currentState = [];
// }
// lastMsg = Date.now();
// };
// Game_Interpreter.prototype.command230 = function () {
// this.wait(this._params[0]);
// Input._currentState = [];
// return true;
// };
// })();