TCG Day Night - Orthographic
Hello! Sorry I didn't post last week, I completely lost track of the days because of Summer and didn't realize I accidentally turned of my reminder to post on my phone. Anyways, this post is going to be a long one, this plugin is the largest plugin I have made for RPG Maker MV. The plugin is a day and night cycle that is based on the system's time. The plugin tints the screen differently whether its day, sunset, or night time. The plugin also has the option to display a clock in the game's menu. Let's dive right in!
There are several plugin parameters and other constants that I use in this plugin, so first things first is to grab the parameters and place them in a variable.
var parameters = PluginManager.parameters('TCGDayNight');
Next, I made a function that converts a string into a tint that RPG Maker MV can use. The tints are an array of 4 integers representing red, green, blue, and grey values. And since all parameters are passed in as strings, I have to make sure the string gets converted into an array in the correct format. To do that, I made a simple function called stringToTint(str).
var stringToTint = function(str) { var arr = str.split(','); var res = []; for(var i = 0; i < 4; i++) { res[i] = Number(arr[i]); } return res; }
I then store all the tints, in an array. I also made some constants to keep track of where in the array each tint is.
var _TINTS = [ stringToTint(parameters['Day Time Tint']), stringToTint(parameters['Sunset Time Tint']), stringToTint(parameters['Night Time Tint']), ]; var _NORMAL = 0; var _SUNSET = 1; var _NIGHT = 2;
Next plugin parameter is the duration of the transition between the tints. I added this to allow the same level of flexibility that RMV provides in the Tint Screen event command.
var _duration = Number(parameters['Tint Duration']);
And the last parameter is a boolean whether or not to show the clock in the menu screen.
var showTimeInMenu = String(parameters['Show Time in Menu']).trim().toLowerCase() === 'true';
Next, I added a boolean and a string to the Game_System class, there is one static instance of Game_System called $gameSystem, so placing different variables here is a good idea if you want them to be accessible anywhere, even within an event. The boolean tells whether or not the game is keeping track of the time in order to tint, this is so that there can be areas in the game where the screen tint does not change based on time. The next is a string that is used in the clock that tells the time in 12 hour format. To make these, I overrode the initialize function in Game_System, making sure to alias the original function.
var _Game_System_initialize = Game_System.prototype.initialize; Game_System.prototype.initialize = function() { _Game_System_initialize.call(this); this._daynight = false; this._timeString = ''; };
The next thing I did is make a getter and setter for the time string. The setter function takes in a date object and formats a string to be used in the clock, or really anywhere the developer wants. The getter simply returns the string.
Game_System.prototype.setTimeString = function(date) { var h = date.getHours(); var m = date.getMinutes(); var s = date.getSeconds(); var ampm = ''; if(h === 0) { ampm = 'AM'; h = 12; } else if(h < 12) { ampm = 'AM'; } else { ampm = 'PM'; if(h != 12) h -= 12; } if(m < 10) { m = '0' + m; } if(s < 10) { s = '0' + s; } this._timeString = h + ':' + m + ':' + s + ' ' + ampm; } Game_System.prototype.getTimeString = function() { return this._timeString; }
Next is are the getters and setters for the dayNight boolean. The dayNight boolean is what is used to determine whether or not to check the time of day to tint the screen.
Game_System.prototype.setDayNight = function(daynight) { this._daynight = daynight; } Game_System.prototype.isDayNight = function() { return this._daynight; }
Next, I added three boolean functions to Game_System that tell whether or not it is a specific time of day, this is to be used anywhere, especially in the plugin option of a conditional branch in an event. For example, there can be a door that only opens during the day, or a character that only triggers a side quest during the night. The functions are isDay(), isSunset(), and isNight().
Game_System.prototype.isDay = function() { var date = new Date(); var hour = date.getHours(); return hour >= 6 && hour < 19; } Game_System.prototype.isSunset = function() { var date = new Date(); var hour = date.getHours(); return hour === 19; } Game_System.prototype.isNight = function() { var date = new Date(); var hour = date.getHours(); return hour >= 20 || hour < 6; }
The next thing is to create the plugin commands. The two plugins commands are startdaynight and stopdaynight. The first command, startdaynight, does not take in any parameters. It turns on the day night cycle. Because the day night cycle is off by default, this must be called at some point in the game from an event in order for the plugin to work. The other command is to stop the cycle, stopdaynight, and it takes in a boolean that determines whether or not to retain the current screen tint. For example, if the boolean is true, and it is night, calling this plugin command will make it look like day, if the boolean is false, or not there, the plugin command will keep it looking like night. You add a plugin command by overriding the pluginCommand function in Game_Interpreter. The function takes a string called command, which is the command, and an array of strings called args, which is everything after the first space of the command, the array is of the strings separated by the spaces.
var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this); if(command.trim().toLowerCase() === 'startdaynight') $gameSystem.setDayNight(true); if(command.trim().toLowerCase() === 'stopdaynight') { var setToNormal = false; if(args[0]) { setToNormal = args[0].trim().toLowerCase() === 'true'; } $gameSystem.setDayNight(false); if(setToNormal) $gameScreen.startTint(_TINTS[_NORMAL], _duration); } };
The next thing is adding some variables to the Scene_Base class. The first two are two integers that determine the current tint, the first one is the tint of the current frame, and the second is the tint of the previous tint. This is to avoid constantly tinting the screen, and only tinting if the current tint based on the time does not match the tint of the previous frame. The next is a flag boolean called startChecked. What this flag does is determines whether or not the initial check has taken place. When first making this, I had an issue with selecting continue from a game that was saved at one point in time, for some reason, the screen was not tinting, so I run a check that compares the current screen tint with the tint that is supposed to be the screen tint, and if they are not equal, just tint the screen to the current tint. I will get into that later, right not I just declare the variables.
var _Scene_Base_initialize = Scene_Base.prototype.initialize; Scene_Base.prototype.initialize = function() { _Scene_Base_initialize.call(this); this._currentTimeTint = _NORMAL; this._previousTimeTint = _NORMAL; this._startChecked = false; };
Next, I overrode the Scene_Base.update() function, to actually run the test that tints the screen. First thing I do is alias and override the update method.
var _Scene_Base_update = Scene_Base.prototype.update; Scene_Base.prototype.update = function() { _Scene_Base_update.call(this); };
Then I create a date object to use for the test, I then also set the time string in Game_System with the same date object.
this._date = new Date(); $gameSystem.setTimeString(this._date);
Then, if $gameSystem.isDayNight() is true, I run a function called updateDayNight, if not, I just set the timeTints to _NORMAL.
if($gameSystem.isDayNight()) { this.updateDayNight(); } else { this._currentTimeTint = _NORMAL; this._previousTimeTint = _NORMAL; }
The final new update function looks like this:
var _Scene_Base_update = Scene_Base.prototype.update; Scene_Base.prototype.update = function() { _Scene_Base_update.call(this); this._date = new Date(); $gameSystem.setTimeString(this._date); if($gameSystem.isDayNight()) { this.updateDayNight(); } else { this._currentTimeTint = _NORMAL; this._previousTimeTint = _NORMAL; } };
Before the updateDayNight() function, there is a function that compares a given tint to the current tint of the screen. I added the function to the Game_Screen class, which has a static instance of $gameScreen in the game. The function is used in the start check. It runs through the two arrays of 4, and if any of their values are not equal, it returns false, and returns true otherwise.
Game_Screen.prototype.compareTint = function(tint) { for(var i = 0; i < 4; i++) { if(this._tone[i] !== tint[i]) return false; } return true; }
The next function is the updateDayNight function that I added to Scene_Base. This function is called ever frame where $gameSystem.isDayNight() is true. The function takes no parameters.
Scene_Base.prototype.updateDayNight = function() { // code goes here
}
First thing I the function does is create a variable that holds the current hour, it gets the hour from the date object created in the update function.
this._hour = this._date.getHours();
Next, if startChecked is not true, it checks if the tints are equal with $gameScreen.compareTint(). If the function returns false, it tints the screen to the current time tint. After checking, it sets startChecked to true, regardless of the result of the comapreTint().
if(!this._startChecked) { if(!$gameScreen.compareTint(_TINTS[this._currentTimeTint])) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._startChecked = true; }
Next, it sets the current time tint based on the time of day. If the current hour is 8 PM or later, or before 6 AM, then it sets the current tint to night. If the hour is 7 PM, then it sets the current tint to sunset. If none of those are true, then it sets the current tint to day.
if(this._hour >= 20 || this._hour < 6) { this._currentTimeTint = _NIGHT; } else if(this._hour === 19) { this._currentTimeTint = _SUNSET; } else { this._currentTimeTint = _NORMAL; }
Finally, it tints the screen if the previous time tint is not equal to the current time tint. After tinting, it stores the current time tint in the previous tint to be used to check with in the next frame.
this._changeTint = this._previousTimeTint != this._currentTimeTint; if(this._changeTint) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._previousTimeTint = this._currentTimeTint;
The full function looks like this:
Scene_Base.prototype.updateDayNight = function() { this._hour = this._date.getHours(); if(!this._startChecked) { if(!$gameScreen.compareTint(_TINTS[this._currentTimeTint])) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._startChecked = true; } if(this._hour >= 20 || this._hour < 6) { this._currentTimeTint = _NIGHT; } else if(this._hour === 19) { this._currentTimeTint = _SUNSET; } else { this._currentTimeTint = _NORMAL; } this._changeTint = this._previousTimeTint != this._currentTimeTint; if(this._changeTint) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._previousTimeTint = this._currentTimeTint; }
The final part of the plugin is the clock in the menu screen. To create the add the clock window, I had to override the create function of Scene_Menu, add a createTimeWindow() function to Scene_Menu, and add an update method to Scene_Menu. The only function in Scene_Menu that I override is the create function, to call createTimeWindow() if the showTimeInMenu boolean from the parameter is true.
var _Scene_Menu_create = Scene_Menu.prototype.create; Scene_Menu.prototype.create = function() { _Scene_Menu_create.call(this); if(showTimeInMenu) this.createTimeWindow(); };
In the createTimeWindow() function, I instantiate a Window_Time object, taht I created, at the origin, then move it up above the gold window, and finally add the window to the scene. Because I created this function, I did not need to alias anything.
Scene_Menu.prototype.createTimeWindow = function() { this._timeWindow = new Window_Time(0, 0); this._timeWindow.y = Graphics.boxHeight - this._goldWindow.height - this._timeWindow.height; this.addWindow(this._timeWindow); }
Most scenes have an update function, that runs some code, then calls the Scene_Base update function. Scene_Menu does not have one by default, so it only calls the Scene_Base update function, however, I need an update function in order to keep the clock constantly accurate as long as the menu is open. It only refreshes the timeWindow if the showTimeInMenu boolean is true.
Scene_Menu.prototype.update = function() { if(showTimeInMenu) this._timeWindow.refresh(); Scene_Base.prototype.update.call(this); }
The final part of the function is creating the Window_Time class. Most windows in RPG Maker are subclasses of Window_Base, and Window_Time is not exception. First thing to do is to create the class and extend the Window_Base class.
function Window_Time() { this.initialize.apply(this, arguments); } Window_Time.prototype = Object.create(Window_Base.prototype); Window_Time.prototype.constructor = Window_Time;
In the Initialize function, I set the width and height of the window, then, call the WIndow_Base initialize, and finally call the refresh method.
Window_Time.prototype.initialize = function(x, y) { var width = this.windowWidth(); var height = this.windowHeight(); Window_Base.prototype.initialize.call(this, x, y, width, height); this.refresh(); };
The next two functions represent the width and height of the window, which I simply copied from the Window_Gold class.
Window_Time.prototype.windowWidth = function() { return 240; }; Window_Time.prototype.windowHeight = function() { return this.fittingHeight(1); };
Next is the refresh function, that simply draws the $gameSystem.getTimeString() onto the window, this is called at each frame.
Window_Time.prototype.refresh = function() { var width = this.contents.width - this.textPadding() * 2; var x = this.windowWidth() - width - this.textPadding(); this.contents.clear(); this.drawText($gameSystem.getTimeString(), x, 0); };
Finally, I create the open function, which is called to open the window when added to the scene.
Window_Time.prototype.open = function() { this.refresh(); Window_Base.prototype.open.call(this); };
And that it is the plugin! The full plugin looks like this:
/*: * * @plugindesc Implements a day/night cycle based on system time * * @author José Rodriguez-Rivas * * @param Tint Duration * @desc Duration of the transition, in frames, between times of day * @default 60 * * @param Day Time Tint * @desc Numbers representing the tint of day time, separated by commas * @default 0,0,0,0 * * @param Sunset Time Tint * @desc Numbers representing the tint of sunset time, separated by commas * @default 68,-34,-34,0 * * @param Night Time Tint * @desc Numbers representing the tint of night time, separated by commas * @default -68,-68,0,68 * * @param Show Time in Menu * @desc Determines whether or not to show the current time in the menu * @default true * * @help * Implements a day/night cycle based on system time * * Parameters: * Tint Duration: * Duration of the transition, in frames, between times of day * Works the same as the Tint Screen Event * Day Time Tint: * Numbers representing the tint of day time, separated by commas * The first number represents the red value, * The second number represents the green value, * The third number represents the blue value, * The fourth number represents the grey value * These values work like the Tint Screen Event * Any number after the fourth will be cut off and ignored * Day time starts at 6 AM and lasts through 6 PM * Sunset Time Tint: * Numbers representing the tint of day time, separated by commas * The first number represents the red value, * The second number represents the green value, * The third number represents the blue value, * The fourth number represents the grey value * These values work like the Tint Screen Event * Any number after the fourth will be cut off and ignored * Sunset time is at 7 PM * Night Time Tint: * Numbers representing the tint of day time, separated by commas * The first number represents the red value, * The second number represents the green value, * The third number represents the blue value, * The fourth number represents the grey value * These values work like the Tint Screen Event * Any number after the fourth will be cut off and ignored * Night time starts at 8 PM and lasts through 5 AM * Show Time in Menu: * Determines whether or not to show the current time in the menu * The time appears above the Gold window in the menu * * Plugin Commands: * startdaynight: * Begins tinting screen based on time, this is off by default * so this command must be called for this plugin to begin working * This command has no parameters * stopdaynight: * Stops tinting screen based on time * Parameters: * Boolean whether or not to reset back to normal * Tint, will use the Day tint in parameters * If false, or not present, the screen will retain current tint * * Additional Inforamtion * Added several boolean functions to Game_System that can be used * in a script call of a conditional branch * $gameSystem.isDayNight() returns true if the game is tinting based on time * $gameSystem.isDay() returns true if the hour is >= 6 and < 19 * $gameSystem.isSunset() returns true if the hour is equal to 19 * $gameSystem.isNight() returns true if the hour >= 20 or < 6 * The current time formatted as in the time in the menu (h:m:s AM/PM) can be * obtained using $gameSystem.getTimeString() * */ (function() { var parameters = PluginManager.parameters('TCGDayNight'); var stringToTint = function(str) { var arr = str.split(','); var res = []; for(var i = 0; i < 4; i++) { res[i] = Number(arr[i]); } return res; } var _TINTS = [ stringToTint(parameters['Day Time Tint']), stringToTint(parameters['Sunset Time Tint']), stringToTint(parameters['Night Time Tint']), ]; var _NORMAL = 0; var _SUNSET = 1; var _NIGHT = 2; var _duration = Number(parameters['Tint Duration']); var showTimeInMenu = String(parameters['Show Time in Menu']).trim().toLowerCase() === 'true'; var _Game_System_initialize = Game_System.prototype.initialize; Game_System.prototype.initialize = function() { _Game_System_initialize.call(this); this._daynight = false; this._timeString = ''; }; Game_System.prototype.setTimeString = function(date) { var h = date.getHours(); var m = date.getMinutes(); var s = date.getSeconds(); var ampm = ''; if(h === 0) { ampm = 'AM'; h = 12; } else if(h < 12) { ampm = 'AM'; } else { ampm = 'PM'; if(h != 12) h -= 12; } if(m < 10) { m = '0' + m; } if(s < 10) { s = '0' + s; } this._timeString = h + ':' + m + ':' + s + ' ' + ampm; } Game_System.prototype.getTimeString = function() { return this._timeString; } Game_System.prototype.setDayNight = function(daynight) { this._daynight = daynight; } Game_System.prototype.isDayNight = function() { return this._daynight; } Game_System.prototype.isDay = function() { var date = new Date(); var hour = date.getHours(); return hour >= 6 && hour < 19; } Game_System.prototype.isSunset = function() { var date = new Date(); var hour = date.getHours(); return hour === 19; } Game_System.prototype.isNight = function() { var date = new Date(); var hour = date.getHours(); return hour >= 20 || hour < 6; } var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this); if(command.trim().toLowerCase() === 'startdaynight') $gameSystem.setDayNight(true); if(command.trim().toLowerCase() === 'stopdaynight') { var setToNormal = false; if(args[0]) { setToNormal = args[0].trim().toLowerCase() === 'true'; } $gameSystem.setDayNight(false); if(setToNormal) $gameScreen.startTint(_TINTS[_NORMAL], _duration); } }; var _Scene_Base_initialize = Scene_Base.prototype.initialize; Scene_Base.prototype.initialize = function() { _Scene_Base_initialize.call(this); this._currentTimeTint = _NORMAL; this._previousTimeTint = _NORMAL; this._startChecked = false; }; var _Scene_Base_update = Scene_Base.prototype.update; Scene_Base.prototype.update = function() { _Scene_Base_update.call(this); this._date = new Date(); $gameSystem.setTimeString(this._date); if($gameSystem.isDayNight()) { this.updateDayNight(); } else { this._currentTimeTint = _NORMAL; this._previousTimeTint = _NORMAL; } }; Game_Screen.prototype.compareTint = function(tint) { for(var i = 0; i < 4; i++) { if(this._tone[i] !== tint[i]) return false; } return true; } Scene_Base.prototype.updateDayNight = function() { this._hour = this._date.getHours(); if(!this._startChecked) { if(!$gameScreen.compareTint(_TINTS[this._currentTimeTint])) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._startChecked = true; } if(this._hour >= 20 || this._hour < 6) { this._currentTimeTint = _NIGHT; } else if(this._hour === 19) { this._currentTimeTint = _SUNSET; } else { this._currentTimeTint = _NORMAL; } this._changeTint = this._previousTimeTint != this._currentTimeTint; if(this._changeTint) { $gameScreen.startTint(_TINTS[this._currentTimeTint], _duration); } this._previousTimeTint = this._currentTimeTint; } var _Scene_Menu_create = Scene_Menu.prototype.create; Scene_Menu.prototype.create = function() { _Scene_Menu_create.call(this); if(showTimeInMenu) this.createTimeWindow(); }; Scene_Menu.prototype.createTimeWindow = function() { this._timeWindow = new Window_Time(0, 0); this._timeWindow.y = Graphics.boxHeight - this._goldWindow.height - this._timeWindow.height; this.addWindow(this._timeWindow); } Scene_Menu.prototype.update = function() { if(showTimeInMenu) this._timeWindow.refresh(); Scene_Base.prototype.update.call(this); } function Window_Time() { this.initialize.apply(this, arguments); } Window_Time.prototype = Object.create(Window_Base.prototype); Window_Time.prototype.constructor = Window_Time; Window_Time.prototype.initialize = function(x, y) { var width = this.windowWidth(); var height = this.windowHeight(); Window_Base.prototype.initialize.call(this, x, y, width, height); this.refresh(); }; Window_Time.prototype.windowWidth = function() { return 240; }; Window_Time.prototype.windowHeight = function() { return this.fittingHeight(1); }; Window_Time.prototype.refresh = function() { var width = this.contents.width - this.textPadding() * 2; var x = this.windowWidth() - width - this.textPadding(); this.contents.clear(); this.drawText($gameSystem.getTimeString(), x, 0); }; Window_Time.prototype.open = function() { this.refresh(); Window_Base.prototype.open.call(this); }; })();
Thank you for reading this long ass post! I feel as project moves along, the plugins will be bigger. I will try to diversify my blog though, maybe a personal post next week? Who knows.
- José Rodriguez-Rivas