TUTORIAL: The Absolute Basics of Plugin Making

● ARCHIVED · READ-ONLY
Started by Faytless 20 posts View original ↗
  1. This will only cover the BASICS of plugin making. I will add more to this when the video is up ( i.e. how to pull params from the user to use in your plugin)

    A video will accommodate this in a little bit. You will NOT become a level 99 Warlock after this... maybe. a level 5 Wizard?.

    Anyways,

    1. Download my template

    View attached files.

    2. Download an IDE, or a Text editor specifically for coding ( I use Notepad ++) (you can use regular note pad, but that's boring)

    https://notepad-plus-plus.org/

    3. Load my Template and read through my comments. You can adjust code if you'd like.

    Believe it or not, this is actually a working plugin as well. Load this to your plugins folder and turn

    on your Text Drawing on your title screen.. see what happens!

    * I will explain this below for the newer guys later tonight* Please read through replies on this thread for errors or best practice.

    Shaz said:
    Moving to Learning Javascript, & pinning

    I think you should highlight the aliasing and calling of aliased methods more. First read through I didn't think you had it in there at all. This should be explained VERY well, and recommended as best practice, otherwise we're going to get a LOT of people writing plugins that just overwrite methods, which is going to give us a lot of incompatible plugins.
    JavaScript:
    //=============================================================================
    // Toms_Example Template
    // by Faytless / Thomas Pham
    // Date: 10/25/2015
    //=============================================================================
    /*:
    * @plugindesc xxxxx  // Describe your plugin
    * @author yyyy       // your name goes here *
    * @param xxxxx      //name of a parameter you want the user to edit
    * @desc yyyyy       //short description of the parameter
    * @default zzzzz    // set default value for the parameter
    */  
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!  
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!  
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!    
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!    
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!      
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!      
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!        
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!        
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!          
    // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!                      
    // Declare your function
    // 1.  change *** to your plug ins file name below.
    // You are telling RPG maker that this plugin exsists.
    (function() {  var parameters = PluginManager.parameters('template');  
        // NOTE: THIS WILL NOT MAKE YOU A DARK WIZARD. HERE ARE SOME BASE FOR YOU TO LAY A FOUNDATION FOR PLUGINS!
        // Now find something you want to edit in the core plugins.  You can
        // find them in the Project\www\js folder
        // 2. find the EXACT function you want to edit    
        /*  This function can be found in rpg_scenes.js  
        Scene_Title.prototype.drawGameTitle = function() {  
            var x = 20;  
            var y = Graphics.height / 4;  
            var maxWidth = Graphics.width - x * 2;  
            var text = $dataSystem.gameTitle;  
            this._gameTitleSprite.bitmap.outlineColor = 'black';  
            this._gameTitleSprite.bitmap.outlineWidth = 8;  
            this._gameTitleSprite.bitmap.fontSize = 72;  
            this._gameTitleSprite.bitmap.drawText(text, x, y, maxWidth, 48, 'center');      
        */          
        // You need to come up with a name for your modification while keeping  
        // class name the same.      
        // the name of the function I want to edit is called    
        //  Scene_Title.prototype.drawGameTitle    
        // The name of my function will be called _Scene_Title_xxx  
        // Follow for ease of use,  follow the template below.  
        // Start your var as _NAME_NAME_YOURCLASSNAME  
        // Have it equal the function you are replacing      
        var _Scene_Title_xxx = Scene_Title.prototype.drawGameTitle;        
        // make your adjustments by adding code, or adjusting them below.
        // This is an exact copy of the code above,  but with some of my adjustments
        // to some of the parameters.  Later,  I will show how you can call your parameters that the user adjusts
        // so your plugin has a little more control      
        Scene_Title.prototype.drawGameTitle = function() {        
            // _Scene_Title_xxx.call(this);  
            //sometimes you have to call your function to get this to work.  In this case you don't Ill explain why later.  
            var x = 20;  
            var y = Graphics.height / 4;  
            var maxWidth = Graphics.width - x * 2;  
            var text = $dataSystem.gameTitle;  
            this._gameTitleSprite.bitmap.outlineColor = 'black';  
            this._gameTitleSprite.bitmap.outlineWidth = 8;  
            this._gameTitleSprite.bitmap.fontSize = 200;  
            this._gameTitleSprite.bitmap.drawText(text, 0,0 , maxWidth, 48, 'center');        
        }          
    })();  // dont touch this.
  2. Great work ! :D

    This will come in handy :)
  3. the syntax (function( ){ })( ); means its an anonymous function and will call itself when the executes that line? oh and on line 35 you mention change "***" what is that referring to? 

    and ty for the post it helps a lot!
  4. fm2107 said:
    the syntax (function( ){ })( ); means its an anonymous function and will call itself when the executes that line? 
    Yep! This methodology is known as a few things, but the name that seems to stick is "Immediately Invoked Function Expression", or IIFE (pronounced "iffy"). If you have a minute, you can check out Ben Alman's explanation for IIFEs here: http://benalman.com/news/2010/11/immediately-invoked-function-expression/

    Anything done inside of that block will be scoped to that function only, so it gives us a safe space to perform our calculations before overriding or adding to behaviors of the game engine or some plugin.

    This technique is used to save us from Global Variable Hell:o
  5. Nice, thank you for the guide! Please, continue it. ;)
  6. Moving to Learning Javascript, & pinning

    I think you should highlight the aliasing and calling of aliased methods more.  First read through I didn't think you had it in there at all.  This should be explained VERY well, and recommended as best practice, otherwise we're going to get a LOT of people writing plugins that just overwrite methods, which is going to give us a lot of incompatible plugins.
  7. Shaz said:
    Moving to Learning Javascript, & pinning

    I think you should highlight the aliasing and calling of aliased methods more.  First read through I didn't think you had it in there at all.  This should be explained VERY well, and recommended as best practice, otherwise we're going to get a LOT of people writing plugins that just overwrite methods, which is going to give us a lot of incompatible plugins.
    Thanks,  you're aboslutly right --  I'll make the changes later tonight

    I was planning on making a small series of videos talking about everything ^^;  I'll condense everything down to this thread when I have a moment.
  8. I'm really excited about this! I know (I think) juuuust enough about Javascript to follow this and learn how to make some sweet plugins. This has already helped explain a lot. Thanks for all the hard work!
  9. Thanks!!
  10. first of all... i'm newbie in JS... i have some question...

    i read on this article:

    http://stackoverflow.com/questions/2421911/what-is-the-purpose-of-wrapping-whole-javascript-files-in-anonymous-functions-li

    doing:

    (function() {...code...})();is useful when we want to have private method / private function...

    but if we don't have those private things... would it be better to write it without it...

    any opinion???

    another thing mentioned in that articles is polluting namespace... also variable inside it cannot be called from outside...

    so if i do:

    (function() {...var est_event_size_Game_System_initialize = Game_System.prototype.initialize;Game_System.prototype.initialize = function() {  est_event_size_Game_System_initialize.call(this);  this._est_custom_passage_map = {};};...})();what i want to ask:

    1) when i create another script... can i use est_event_size_Game_System_initialize again as variable name for another alias?

    since it's private variable thus not recognized by another plugin...

    2)if i create another script... can i call est_event_size_Game_System_initialize.call(this);?

    or i can only do that in the first script.

    sorry for newbie question... the answer will be useful because when the plugin i write is still not much... i can still edit it for better performance.
  11. estriole said:
    what i want to ask:

    1) when i create another script... can i use est_event_size_Game_System_initialize again as variable name for another alias?

    since it's private variable thus not recognized by another plugin...

    2)if i create another script... can i call est_event_size_Game_System_initialize.call(this);?

    or i can only do that in the first script.

    sorry for newbie question... the answer will be useful because when the plugin i write is still not much... i can still edit it for better performance.
    I'm a newbie in js myself. But I think I've got the basics. If I'm completly off, than surely someone with more expertise will correct it :)

    1) yes.

    2) if you have another alias in the second script with the same name, why not?

    est_event_size_Game_System_initialize.call(this); calls the function that you override with your alias.
  12. I've been trying to follow along with this template to make a menu. I'm starting small, and just trying to test things out at the moment and I've already hit a wall.

    To start I just wanted to edit what the things in the command window do, pretty much just so I could tell that my code was actually doing something, which at this point doesn't seem to be working.

    Here's what I have so far.

      (function() {  var parameters = PluginManager.parameters('vhsMenu'); var _Scene_Menu_vhsMenu = Scene_Menu.prototype.createCommandWindow; Scene_Menu.prototype.createCommandWindow = function() { _Scene_Menu_vhsMenu.call(this); this._commandWindow.setHandler('item', this.commandItem.bind(this)); this._commandWindow.setHandler('skill', this.commandPersonal.bind(this)); this._commandWindow.setHandler('equip', this.commandPersonal.bind(this)); this._commandWindow.setHandler('status', this.commandPersonal.bind(this)); this._commandWindow.setHandler('formation', this.commandFormation.bind(this)); this._commandWindow.setHandler('options', this.commandOptions.bind(this)); this._commandWindow.setHandler('save', this.commandSave.bind(this)); this._commandWindow.setHandler('gameEnd', this.commandSave.bind(this)); this._commandWindow.setHandler('cancel', this.popScene.bind(this)); this.addWindow(this._commandWindow);};It looks to me like I've done everything right, and that the Game End command should do the same thing as the Save command (which I know is pointless, I'm just trying stuff out).

    Maybe menus are not a good place to start? Am I missing something insanely obvious?

    Thanks guys.
  13. (function() { var parameters = PluginManager.parameters('vhsMenu'); var _Scene_Menu_vhsMenu = Scene_Menu.prototype.createCommandWindow; Scene_Menu.prototype.createCommandWindow = function() { _Scene_Menu_vhsMenu.call(this); this._commandWindow.setHandler('item', this.commandItem.bind(this)); this._commandWindow.setHandler('skill', this.commandPersonal.bind(this)); this._commandWindow.setHandler('equip', this.commandPersonal.bind(this)); this._commandWindow.setHandler('status', this.commandPersonal.bind(this)); this._commandWindow.setHandler('formation', this.commandFormation.bind(this)); this._commandWindow.setHandler('options', this.commandOptions.bind(this)); this._commandWindow.setHandler('save', this.commandSave.bind(this)); this._commandWindow.setHandler('gameEnd', this.commandSave.bind(this)); this._commandWindow.setHandler('cancel', this.popScene.bind(this)); this.addWindow(this._commandWindow); }})();for got the   })(); .

    keep in mind that this plugin is going to REPLACE the function

    i havent had a chance to update this thread yet.  Let me know if you have any questions
  14. Please, continue the tutorials!
  15. Faytless said:
    (function() { var parameters = PluginManager.parameters('vhsMenu'); var _Scene_Menu_vhsMenu = Scene_Menu.prototype.createCommandWindow; Scene_Menu.prototype.createCommandWindow = function() { _Scene_Menu_vhsMenu.call(this); this._commandWindow.setHandler('item', this.commandItem.bind(this)); this._commandWindow.setHandler('skill', this.commandPersonal.bind(this)); this._commandWindow.setHandler('equip', this.commandPersonal.bind(this)); this._commandWindow.setHandler('status', this.commandPersonal.bind(this)); this._commandWindow.setHandler('formation', this.commandFormation.bind(this)); this._commandWindow.setHandler('options', this.commandOptions.bind(this)); this._commandWindow.setHandler('save', this.commandSave.bind(this)); this._commandWindow.setHandler('gameEnd', this.commandSave.bind(this)); this._commandWindow.setHandler('cancel', this.popScene.bind(this)); this.addWindow(this._commandWindow); }})();for got the   })(); .

    keep in mind that this plugin is going to REPLACE the function

    i havent had a chance to update this thread yet.  Let me know if you have any questions
    I actually had the })(); at the end of the script, I just forgot to paste it. I think the problem was actually an extra "};" in there, but regardless, following along with your code there fixed it! Now I can start messing with menu things, yay!

    Please continue the tutorials! Thank you!
  16. estriole said:
    is useful when we want to have private method / private function...

    but if we don't have those private things... would it be better to write it without it...

    any opinion???
    You generally always want to box off your code as much as possible to avoid any conflicts, only making things global if you know you need it.If you really need a global variable or function, use window as the main object. It allows you to have private variables while using them together with global ones. Or, you define the globals outside the function.

    Code:
    var doVelma;+function(w) {  var catchphrase = 'Jinkies!';  doVelma = function() {    catchphrase = 'Jinkies!';  }  window.doDaphne = function() {    catchphrase = 'Jeepers!';  }  w.getGhost = function() {    console.log(catchphrase);  }}(window);
    As you can see I used the wrapper function in conjunction with a parameter, and I passed window as an argument, making w its shortcut.
  17. GaryCXJk said:
    You generally always want to box off your code as much as possible to avoid any conflicts, only making things global if you know you need it.

    If you really need a global variable or function, use window as the main object. It allows you to have private variables while using them together with global ones. Or, you define the globals outside the function.

    var doVelma;+function(w) { var catchphrase = 'Jinkies!'; doVelma = function() { catchphrase = 'Jinkies!'; } window.doDaphne = function() { catchphrase = 'Jeepers!'; } w.getGhost = function() { console.log(catchphrase); }}(window);As you can see I used the wrapper function in conjunction with a parameter, and I passed window as an argument, making w its shortcut.
    i decide to make ALL my alias global... since if it's private... then the 'original' function will be lost and cannot be accessed...

    (correct me though if i'm wrong... and please tell me how to access that 'original' function)

    just found some thing that need me to call 'original' method before aliased by yanfly in his message core plugin. and fortunately yanfly didn't make it private variable like lots of plugin maker here... so i can just call that original method to make my plugin compatible with yanfly's.
  18. estriole said:
    i decide to make ALL my alias global... since if it's private... then the 'original' function will be lost and cannot be accessed...

    (correct me though if i'm wrong... and please tell me how to access that 'original' function)

    just found some thing that need me to call 'original' method before aliased by yanfly in his message core plugin. and fortunately yanfly didn't make it private variable like lots of plugin maker here... so i can just call that original method to make my plugin compatible with yanfly's.
    Ok, I'll correct you  :D

    You can call the "original" function in a private alias in the same way as a global one.

    yourAliasedFunction.call(this[, + needed argument]);

    here's an example from the EnemyBook plugin:

    var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this, command, args);If Yanfly had made them private, there would be no way to add on to the plugin like the ItemUpgradeSlots plugin for the ItemCore plugin. B)
  19. kiriseo said:
    Ok, I'll correct you  :D

    You can call the "original" function in a private alias in the same way as a global one.

    yourAliasedFunction.call(this[, + needed argument]);

    here's an example from the EnemyBook plugin:

    var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand; Game_Interpreter.prototype.pluginCommand = function(command, args) { _Game_Interpreter_pluginCommand.call(this, command, args);If Yanfly had made them private, there would be no way to add on to the plugin like the ItemUpgradeSlots plugin for the ItemCore plugin. B)
    no... i mean something like this:

    (function() {    var est_test = Game_Event.prototype.initialize    Game_Event.prototype.initialize = function(mapId, eventId) {        est_test.call(this,mapId,eventId);    };})();est_test become private variable...

    then in ANOTHER plugin below the first one i write:

       var another_alias = Game_Event.prototype.initialize    Game_Event.prototype.initialize = function(mapId, eventId) {       if(somecondition here)       {          est_test.call(this,mapId,eventId);       }else{          another_alias.call(this,mapId,eventId)       }    };est_test is lost... (it's the original function before i alias it in first code) and in second plugin it throw errors...

    another_alias will call the edited function... not the original one.

    or like i said earlier... any way to access est_test from another plugin???

    if i write it globally

        var est_test = Game_Event.prototype.initialize    Game_Event.prototype.initialize = function(mapId, eventId) {        est_test.call(this,mapId,eventId);    };second code above works fine...

    the problem is i see some scripter wrapping their plugins with

    (function() {})();would it be trouble for compatibility patch?

    if you said no way we need original function before aliasing... the fact is we do sometimes...

    this is taken from my plugin EST - AUTOCOLOR PLUS

    before it has compatibility issues with yanfly message core... specifically the auto wrap part...

    it's because before i auto color the text... i convert escape character so \n[1] will translate to Doraemon (if your actor doraemon).

    and will be affected by the auto color... then the final result is thrown again to convert escape character to manage the coloring...

    in yanfly... it broke the autowrap... so i need to call the original method before yanfly alias it for my first convert escape character...

    if yanfly put it in function wrap like above... thus making the alias lost... then i cannot do that... and it won't be compatible easily.

    Code:
    var est_auto_text_color_Window_Base_convertEscapeCharacters =                             Window_Base.prototype.convertEscapeCharacters;Window_Base.prototype.convertEscapeCharacters = function(text) {  if (EST.AutoColor.OnlyUseInMessage && this.constructor != Window_Message)       return est_auto_text_color_Window_Base_convertEscapeCharacters.call(this,text);    var xtext = est_auto_text_color_Window_Base_convertEscapeCharacters.call(this,text);  if(Imported.YEP_MessageCore) xtext = Yanfly.Message.Window_Base_convertEscapeCharacters.call(this, text);    var word, use, regex;    for (key in $gameSystem._AC_setting)    {        regex = new RegExp("\\b("+String(key)+")\\b",'img');        xtext = xtext.replace(regex, function(x){            use = $gameSystem.AutoCorrectOk() ? key : x;            if ($gameSystem.AutoColorOk())                   return "\\c["+$gameSystem._AC_setting[key]+"]"+use+"\\c["+EST.AutoColor.ReturnColor+"]";            if (!$gameSystem.AutoColorOk()) return use;        });    }    if(EST.AutoColor.OtherTextColor.toUpperCase() === 'TRUE')               xtext = "\\c["+EST.AutoColor.ReturnColor+"]"+xtext+"\\c["+EST.AutoColor.ReturnColor+"]";    xtext = est_auto_text_color_Window_Base_convertEscapeCharacters.call(this,xtext);    return xtext;};
  20. estriole said:
    no... i mean something like this:

    (function() {    var est_test = Game_Event.prototype.initialize    Game_Event.prototype.initialize = function(mapId, eventId) {        est_test.call(this,mapId,eventId);    };})();est_test become private variable...

    then in ANOTHER plugin below the first one i write:

       var another_alias = Game_Event.prototype.initialize    Game_Event.prototype.initialize = function(mapId, eventId) {       if(somecondition here)       {          est_test.call(this,mapId,eventId);       }else{          another_alias.call(this,mapId,eventId)       }    };est_test is lost... (it's the original function before i alias it in first code) and in second plugin it throw errors...

    another_alias will call the edited function... not the original one.

    or like i said earlier... any way to access est_test from another plugin???
    I think, we meant different things here.

    When you said "original function" I supposed you mean the, well, original Game_Event.prototype.initialize function from MV, in this case.

    Not some function someone aliased somewhere else.

    That doesn't work if it the first alias is private, of course.

    That's why Yanfly doesn't make the functions private as they would be needed for plugin extensions as the example with the ItemUpgradeSlots plugin I made.